How do you get the logical xor of two variables in Python?
Solution 1
If you're already normalizing the inputs to booleans, then != is xor.
bool(a) != bool(b)
Solution 2
You can always use the definition of xor to compute it from other logical operations:
(a and not b) or (not a and b)
But this is a little too verbose for me, and isn't particularly clear at first glance. Another way to do it is:
bool(a) ^ bool(b)
The xor operator on two booleans is logical xor (unlike on ints, where it's bitwise). Which makes sense, since bool
is just a subclass of int
, but is implemented to only have the values 0
and 1
. And logical xor is equivalent to bitwise xor when the domain is restricted to 0
and 1
.
So the logical_xor
function would be implemented like:
def logical_xor(str1, str2):
return bool(str1) ^ bool(str2)
Credit to Nick Coghlan on the Python-3000 mailing list.
Solution 3
Bitwise exclusive-or is already built-in to Python, in the operator
module (which is identical to the ^
operator):
from operator import xor
xor(bool(a), bool(b)) # Note: converting to bools is essential
Solution 4
As Zach explained, you can use:
xor = bool(a) ^ bool(b)
Personally, I favor a slightly different dialect:
xor = bool(a) + bool(b) == 1
This dialect is inspired from a logical diagramming language I learned in school where "OR" was denoted by a box containing ≥1
(greater than or equal to 1) and "XOR" was denoted by a box containing =1
.
This has the advantage of correctly implementing exclusive or on multiple operands.
- "1 = a ^ b ^ c..." means the number of true operands is odd. This operator is "parity".
- "1 = a + b + c..." means exactly one operand is true. This is "exclusive or", meaning "one to the exclusion of the others".
Solution 5
- Python logical
or
:A or B
: returnsA
ifbool(A)
isTrue
, otherwise returnsB
- Python logical
and
:A and B
: returnsA
ifbool(A)
isFalse
, otherwise returnsB
To keep most of that way of thinking, my logical xor definintion would be:
def logical_xor(a, b):
if bool(a) == bool(b):
return False
else:
return a or b
That way it can return a
, b
, or False
:
>>> logical_xor('this', 'that')
False
>>> logical_xor('', '')
False
>>> logical_xor('this', '')
'this'
>>> logical_xor('', 'that')
'that'
Zach Hirsch
Updated on April 30, 2022Comments
-
Zach Hirsch about 2 years
How do you get the logical xor of two variables in Python?
For example, I have two variables that I expect to be strings. I want to test that only one of them contains a True value (is not None or the empty string):
str1 = raw_input("Enter string one:") str2 = raw_input("Enter string two:") if logical_xor(str1, str2): print "ok" else: print "bad"
The
^
operator seems to be bitwise, and not defined on all objects:>>> 1 ^ 1 0 >>> 2 ^ 1 3 >>> "abc" ^ "" Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for ^: 'str' and 'str'
-
mmx over 15 yearsHow do you define "xor" for a couple of strings? What do you feel "abc" ^ "" should return that it doesn't?
-
Zach Hirsch over 15 yearsIt should return True, rather than raise an exception, since only one of the strings is True as defined by normal Python's bool type.
-
Wowbagger and his liquid lunch over 11 yearsI'm amazed that Python doesn't have an infix operator called "xor", which would be the most intuitive, Pythonic implementation. Using "^" is consistent with other languages, but not as blatantly readable as most of Python is.
-
Kaz almost 11 years@MehrdadAfshari The obvious answer to your question is that
a xor a
is defined as(a and not b) or (not a and b)
, and soa xor b
, whena
andb
are character strings, or any other types, should yield whatever(a and not b) or (not a and b)
yields. -
mckenzm about 9 yearsThe issue is that documentation is poor. ^ is "bitwise exclusive or", which literally interpreted means bit by bit, not bool by bool. so x'FFFF00' ^ x'FFFF00' should be x'000000'. Or is this only meant to occur on a char by char basis ? cast as numbers ? We need to iterate the shorter string characters to match the length of the longer string. All this should be built in.
-
Guimoute over 2 years@mckenzm If that helps your point,
int("0xFFFF00", 16) ^ int("0xFFFF00", 16) == int(0)
so yes you have to cast as number and callhex
on the result, then pad the result with enough 0 on the left to reach the longest length. -
mckenzm over 2 years@Guimoute Thanks, a bit of work involved, but doable with filling/alignment.
-
-
nosklo over 15 yearsthat would return True for xor('this', '') and to follow python's way, it should return 'this'.
-
user1066101 over 15 years@nosklo: Take it up with the BDFL, please, not me. Since Python returns True, then that must be Python's way.
-
nosklo over 15 yearsI mean for consistency with the other python logical operators - Python doesn't return True when I do ('this' or ''), it returns 'this'. But in your function xor('this', '') returns True. It should return 'this' as the "or" python builtin does.
-
Zach Hirsch over 15 yearsThis seems bad, or at least weird, to me. None of the other built-in logical operators return one of three possible values.
-
Ishbir over 15 yearsSo, True + True + False + True == 3, and 3 != 1, but True XOR True XOR False XOR True == True. Can you elaborate on "correctly implementing XOR on multiple operands"?
-
Ishbir over 15 yearsPython
and
andor
do short-circuit. Anyxor
implementation can't short-circuit, so there is already a discrepancy; therefore, there is no reason thatxor
should operate likeand
+or
do. -
SingleNegationElimination almost 15 yearsgreat post, but of all ways to name your parameters, why 'str1' and 'str2'?
-
nosklo over 14 years@Zach Hirsch: That's why I said "to keep most of that way of thinking" - since there's no good result when both are true or false
-
orokusaki over 14 years@Token why not. Do you mean because they aren't very Pythonic?
-
SingleNegationElimination over 14 years@orokusaki: because they don't seem to suggest their intended use very well.
-
orokusaki over 14 years@Zach Hirsch Could you use (not a and b) instead of (b and not a) for readability or would the definition be inconsistent with xor.
-
BlueRaja - Danny Pflughoeft about 14 years@ΤΖΩΤΖΙΟΥ: Wrap it in a
bool()
-
Denis Barmenkov about 13 yearsLogical operation must return logical value, so second "return a or b" looks strange, so second return must return True.
-
nosklo about 13 years@Denis Barmenkov: Well, note that python logical operators
and
andor
won't return logical value.'foo' and 'bar'
returns'bar'
... -
rjmunro almost 13 yearsYou should put the nots first like this
(not b and a) or (not a and b)
so that it returns the string if there was one, which seems like the pythonic way for the function to operate. -
Erik Kaplun over 12 yearsAt first sight, the 2 previous answers seem like the best, but on second thought, this one is actually the only truly correct one, i.e. it's the only one that provides an example of a
xor
implementation that is consistent with the built-inand
andor
. However, of course, in practical situations,bool(a) ^ bool(b)
or evena ^ b
(ifa
andb
are known to bebool
) are more concise of course. -
Admin about 12 yearsAlthough this is clever and short, I'm not convinced it's clean. When someone reads this construct in the code, is it immediately obvious to them that this is an xor operation? I felt obliged to add a comment - a sign for me that I'm writing unclear code and try to apologise with a comment.
-
Niriel about 12 yearsoperator.xor corresponds to the bitwise operation, which it the one that the original poster doesn't want.
-
AmigoNico almost 12 yearsPerhaps "is it clear that it's an XOR?" is the wrong question. We were just trying to see whether the answer to two questions are the same, and thinking we'd use XOR to implement that. For example, if we want to ensure that we are not comparing apples to oranges, is "if xor( isApple(x), isApple(y) )" really clearer than "if isApple(x) != isApple(y)" ? Not to me!
-
user541686 almost 12 years@TokenMacGuy: What were you suggesting he should name them instead?
-
ril3y almost 12 yearsThis is what I needed. When reverse engineering malware lots of times strings are mangled until an XOR operation. Using this chr(xor(ord("n"), 0x1A)) = 't'
-
elmo almost 12 yearsThere is problem with using "!=" as xor. You would probably expect bool(a) != bool(b) != bool(c) to be the same as bool(a) ^ bool(b) ^ bool(c). So do casts to bool, but I would recommend ^. To know what's going up in the first example look up "operator chaining".
-
ely over 11 years@tzot Your example fails, because according to ddaa's solution, you apply the addition on only two variables at a time. So the right way to write it all out would have to be
(((((True + True)==1)+False)==1)+True)==1
. The answer given here totally generalizes to multiple operands. -
ely over 11 yearsAlso, there's a difference between a three-way XOR vs. order-of-operations-grouped set of two XORs. So 3-WAY-XOR(A,B,C) is not the same thing as XOR(XOR(A,B),C). And ddaa's example is the former, while yours assumes the latter.
-
Ishbir over 11 years@EMS: thanks. Your objections clarified (to me) what ddaa meant.
-
Wowbagger and his liquid lunch over 11 years@elmo: +1 for pointing out the difference, and +1 for teaching me what operator chaining is! I am in the camp that says that != is not as readable as ^.
-
askewchan over 10 yearsBe careful, this is also bitwise:
xor(1, 2)
returns3
. From the docstring:xor(a, b) -- Same as a ^ b.
Remember that anything imported fromoperator
is just a functional form of an existing builtin infix operator. -
Martijn Pieters over 9 years@askewchan: The
bool
type overloads__xor__
to return booleans. It'll work just fine, but its overkill whenbool(a) ^ bool(b)
does exactly the same thing. -
pathunstrom about 9 yearsFor improving this answer:
(bool(False) is False) == True
. You can just useFalse
on those lines. -
jpmc26 over 8 years@Mr.F Your explanation doesn't really excuse this answer. In Python, if you just do
True + True + False + True
, you do get3
, andTrue + True + False + True == 3
gives backTrue
whileTrue + True + False + True == 1
gives backFalse
. In other words, the answer here doesn't generalize correctly; for it to do so, you need to do additional work. Meanwhile a simpleTrue ^ True ^ False ^ True
works as expected. -
ely over 8 years@jpmc26 I do not understand your comment. The addition approach is meant to generalize the operation in which you want to check that exactly one operand is
True
, a multi-arity XOR. This is a different operation than, for example,A XOR B XOR ... XOR Z
. In other words, if you plan to use the addition-based version, then upon submitted the operands inTrue + True + False + True
you should expect the result to beFalse
since more than one of those isTrue
, which works if the condition checks for== 1
. -
ely over 8 yearsSo when you write
True + True + False + True == 1
gives backFalse
that's correct and desired if you're using the addition approach. Maybe I'm misunderstanding your point, but I don't see what's wrong. Whether a person wants one or the other operation is up to them, and for multi-arity XOR, it's just a matter of semantics as to which of these "is XOR". -
minmaxavg over 8 years(bool(a) + bool(b) + bool(c)) % 2 == 1 would work but it's not worth
^
-ing. -
mrucci over 7 yearsJust a tip for the ingenuous like me: be careful with operator precedence! I wanted to check if two variables were both null or both non-null and ended up writing
(a is None != b is None)
. Well, that's wrong! -
RNA about 7 yearsshould it be
bool(a) is not bool(b)
instead? -
Alan over 6 yearsDoes the language guarantee that bool(a)^bool(b) will be a bool (rather than just an int)? If so, where is the guarantee documented? Thanks.
-
Alan over 6 years@ddaa I don't see why. It is a crucial consideration for your first proposed answer.
-
Alan over 6 years@ddaa Let's just say it is the assumption embodied in the current behavior of CPython. I'm just asking if you know what type your first proposed answer will return in any valid Python implementation. It is a simple question, even if you have some personal aversion to
is
. -
ddaa over 6 yearsYou are assuming there is such a thing as a "valid Python implementation". There is no such thing as far as I know. Therefore your question is invalid.
-
Quantum7 about 6 years@MartijnPieters The
^
operator calls__xor__
internally. -
Martijn Pieters about 6 years@Quantum7: yes, I'm not sure why you are telling me this though. I just said that the
bool
type implements the__xor__
method specifically because^
calls it. The point being thatbool(a) ^ bool(b)
works fine, there is no need to use theoperator.xor()
function here. -
Jason about 6 yearsIs this the real answer? I try this code on my machine,
print bool("Hellow World") == bool("H")
, it returnsTrue
-
DylanYoung over 5 years@Jason You're using
==
instead of!=
. You can verify by brute force if necessary:for b1, b2 in ((True, False), (True, True), (False, False): assert (b1 != b2) == (b1 ^ b1)
. This is verification by truth table (noting the commutativity of the operator). I'll leave it as an exercise to verify using inference rules ;) -
Mayou36 over 5 yearsor
xor("hey", "there")
>>> True, but that's not what we want -
ɹɐʎɯɐʞ about 5 yearsNot only this make the code hard to understand, as pointed out by @elmo this is NOT equivalent to xor operation and can lead to hard to spot bugs in the code. operator.xor would be a better alternative.
-
yucer about 5 yearsI also use to read it as "strictly different". That's because some languages use to implement the operation bit by bit of the binary representation and take the bool of the resulting bitwise operation. I guess your answer is more "type-bulletproofed" because it extends beyond the boolean space.
-
yucer about 5 yearsI mean the fact that your answer cover the case of comparing None, False, '' as different is the distinctive stuff. For example: bool(False) != bool('') nevertheless False is not ''" agrees more with this semantics of "strictly different"
-
jpmc26 almost 5 yearsI was thinking about saying something about both inputs being invoked before the function is called (which is often a downside of implementing your own boolean functions), but then I remembered that xor can't short circuit anyway. You might want to make note of that.
-
Arel almost 5 yearsYour answer should make clear that
^
is a bitwise xor (not logical xor like the question asked).bool(2) ^ bool(3)
gives a different answer thanbool(2 ^ 3)
. -
Arel almost 5 yearsDownvoting because this answer is WRONG (since as pointed out by @elmo
!=
is not XOR in python) -
Arel almost 5 yearsThat's 100 ns of my life I won't get back ;-)
-
Arel almost 5 years@kojiro evidently so!
-
Alfe over 4 years@Arel But that's not the case.
a ^ b
is polymorph. Ifa
andb
arebool
instances, the result will bebool
as well. This behavior can hardly be called a "bitwise" xor. -
Arel over 4 years@Alfe the important point is that values must be cast to booleans first. The Python documentation defines
^
as bitwise, even though it's an interesting point that types are preserved forbool
andint
types. Note:True ^ 2
is3
, demonstrating how it is indeed bitwise. -
Alfe over 4 years@Arel Yes, the
bool ^ int
case is kind of casting everything toint
first. Still, Python has built-in the^
operator for many bits inint
and for the one bit represented in abool
, so both are bitwise, but the bitwise xor for a single bit just is the logical xor for booleans. -
warvariuc over 4 years
sum(map(bool, y)) % 2 == 1
-
Nicholas Hamilton over 4 yearsI always hate using this operator, although I understand it is
xor
, coming from an engineering background, to me this instinctively feels like a mathematical power, ie2^3 = pow(2,3)
which means I always explicitly comment to prevent confusion. -
ShadowRanger over 4 years@Arel: For
bool
, for a single comparison,!=
is exactly the same as xor. It's only for the chained case where the behavior differs. Not worth a downvote. -
ShadowRanger over 4 yearsFor an in-between timing, you can do
from operator import truth
at the top of the module, and testtruth(a) != truth(b)
.bool
being a constructor has a lot of unavoidable overhead at the C level (it must accept arguments as the equivalent of*args, **kwargs
and parse thetuple
anddict
to extract them), wheretruth
(being a function) can use an optimized path that doesn't require either atuple
or adict
, and runs in about half the time ofbool
based solutions (but still longer thannot
based solutions). -
Arel over 4 years@ShadowRanger xor (
^
) is xor. Not-equals (!=
) is not xor in Python because of a subtle reason that could have been clarified in the answer but wasn't. You're right that it works in the two-variable case like the question asks, but the assertion that!=
is xor is inaccurate and unnecessary. I tried to edit this question (twice) to add a clarification, but reviewers rejected my changes. Hence, downvote. -
DylanYoung about 4 yearsWhy blame the respondent for poor reviewers?
-
DylanYoung about 4 yearsIndeed there is. That's why you can run the same python program on multiple differently implemented interpreters
-
wjandrea almost 4 years@RNA No, don't compare booleans with
is
. This answer explains why in the second section. PEP 8 also mentions it, but doesn't explain. -
Josiah Yoder almost 4 years@wjandrea That post seems incomplete for
bool(x) is True
. We can be sure the value returned by bool will be either True or False. -
wjandrea almost 4 years@Josiah Yes, but we can't be sure that
True is True
because that's an implementation detail. -
Josiah Yoder almost 4 years
-
wjandrea almost 4 years@Josiah Oh, huh, the answer I linked must be wrong, or out of date. Still, equality comparison is more intuitive than identity comparison, even though they do the same thing for two bools.
-
Nearoo over 3 yearsYou could even drop the
abs
, python interprets negative numbers as truthy, although this is very obscure imo (what doesif (x > 1) - (y > 3)
mean? -
Boris Verkhovskiy about 3 yearsI see little reason to use
sum
if you just have 2 variables,bool(a) + bool(b) == 1
does the same thing. -
Guimoute about 3 yearsYour answer
bool(a) ^ bool(b)
shows much more clearly the xor intent of the programmer than the currently accepted answer ofbool(a) != bool(b)
. -
alan2here about 3 years^ commonly means "to the power of". So it might look kind of like "True to the power of False = True". Bear in mind that True != 1 even if that's how it works in Python and C++.
-
Lutz Prechelt almost 3 yearsWhich version of which Python implementation does this refer to?
-
Rugnar almost 3 years@LutzPrechelt unfortunately I don't remember; Probably 3.5
-
c z over 2 years@Boris Potayto potarto
-
Boris Verkhovskiy over 2 years@cz I'm glad that you agree that one is clearly wrong :)
-
JJL over 2 yearsFor a single comparison, this construction has the advantage of interpreting
None
asFalse
whereas^
onNone
throws aTypeError
-
Edward Spencer over 2 years@MartijnPieters, sometimes using a method like
operator.xor
is useful because it's an object that can be passed around. Obviously you could write your own method, but why do that when there's already a built-in? -
Martijn Pieters over 2 years@EdwardSpencer: absolutely, when you need to parametrize the expression, the
operator
module is fantastic. But the way this answer was worded when it was first posted and before Arel adding in the^
operator, the answer seemed to imply thatoperator.xor()
was the only way to do this. -
Clément over 2 years
not not A
is another way to get the same result asbool(A)
without a function call. -
Ed Griebel over 2 yearsAddition to get XOR obtuse and unclear, violating PEP 20 "Explicit is better than implicit. Simple is better than complex.", downvoting
-
Vladimir Vilimaitis about 2 yearsThis is pretty clever, I like it.