954

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 an 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 is 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'
taylorSeries
  • 505
  • 2
  • 6
  • 18
Zach Hirsch
  • 24,631
  • 8
  • 32
  • 29
  • 6
    How do you define "xor" for a couple of strings? What do you feel "abc" ^ "" should return that it doesn't? – Mehrdad Afshari Jan 11 '09 at 12:39
  • 24
    It should return True, rather than raise an exception, since only one of the strings is True as defined by normal Python's bool type. – Zach Hirsch Jan 11 '09 at 19:10
  • 70
    I'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. – Mark E. Haase Nov 11 '12 at 03:50
  • 19
    @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 so `a xor b`, when `a` and `b` are character strings, or any other types, should yield whatever `(a and not b) or (not a and b)` yields. – Kaz May 14 '13 at 15:57
  • 2
    The 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. – mckenzm Mar 22 '15 at 02:29
  • @mckenzm If that helps your point, `int("0xFFFF00", 16) ^ int("0xFFFF00", 16) == int(0)` so yes you have to cast as number and call `hex` on the result, then pad the result with enough 0 on the left to reach the longest length. – Guimoute Sep 16 '21 at 12:09

29 Answers29

1636

If you're already normalizing the inputs to booleans, then != is xor.

bool(a) != bool(b)
A. Coady
  • 54,452
  • 8
  • 34
  • 40
  • 227
    Although 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. –  Mar 19 '12 at 17:25
  • 68
    Perhaps "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! – AmigoNico May 21 '12 at 18:27
  • 133
    There 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". – elmo Jul 25 '12 at 11:50
  • 25
    @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 ^. – Mark E. Haase Nov 11 '12 at 03:58
  • 3
    Just 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! – mrucci Sep 22 '16 at 13:13
  • 22
    should it be `bool(a) is not bool(b)` instead? – RNA Feb 14 '17 at 01:35
  • Is this the real answer? I try this code on my machine, `print bool("Hellow World") == bool("H")`, it returns `True` – Jason Apr 27 '18 at 17:07
  • 3
    @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 ;) – DylanYoung Dec 07 '18 at 16:29
  • 5
    Not 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](http://docs.python.org/library/operator.html#operator.xor) would be a better alternative. – ɹɐʎɯɐʞ Feb 13 '19 at 16:58
  • 1
    Downvoting because this answer is WRONG (since as pointed out by @elmo `!=` is not XOR in python) – Arel Jul 25 '19 at 12:38
  • 10
    @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 Sep 26 '19 at 18:12
  • @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. – Arel Nov 01 '19 at 02:01
  • 1
    Why blame the respondent for poor reviewers? – DylanYoung Mar 09 '20 at 14:14
  • 1
    @RNA No, don't compare booleans with `is`. [This answer explains why in the second section](https://stackoverflow.com/a/9494887/4518341). [PEP 8 also mentions it, but doesn't explain](https://www.python.org/dev/peps/pep-0008/#programming-recommendations). – wjandrea Jul 13 '20 at 23:02
  • @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. – Josiah Yoder Jul 27 '20 at 18:52
  • 1
    @Josiah Yes, but we can't be sure that `True is True` because that's an implementation detail. – wjandrea Jul 27 '20 at 19:13
  • 4
    @wjandrea `True is True` is [guaranteed by the Python language reference](https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy). [Thanks Pedro, for asking](https://stackoverflow.com/questions/24842852/is-there-only-one-true-and-one-false-object-in-python) – Josiah Yoder Jul 27 '20 at 19:16
  • 1
    @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. – wjandrea Jul 27 '20 at 19:23
  • For a single comparison, this construction has the advantage of interpreting `None` as `False` whereas `^` on `None` throws a `TypeError` – JJL Aug 25 '21 at 16:50
673

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.

Zach Hirsch
  • 24,631
  • 8
  • 32
  • 29
  • 15
    great post, but of all ways to name your parameters, why 'str1' and 'str2'? – SingleNegationElimination Jul 04 '09 at 16:55
  • 2
    @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. – orokusaki Feb 01 '10 at 15:18
  • 11
    You 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. – rjmunro May 07 '11 at 21:06
  • I 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. – jpmc26 Jul 15 '19 at 18:49
238

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
Arel
  • 1,339
  • 17
  • 22
singingwolfboy
  • 5,336
  • 3
  • 27
  • 32
  • 4
    This 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' – ril3y Jul 11 '12 at 14:15
  • 97
    Be careful, this is also bitwise: `xor(1, 2)` returns `3`. From the docstring: `xor(a, b) -- Same as a ^ b.` Remember that anything imported from `operator` is just a functional form of an existing builtin infix operator. – askewchan Sep 15 '13 at 16:59
  • 10
    @askewchan: The `bool` type overloads `__xor__` to return booleans. It'll work just fine, but its overkill when `bool(a) ^ bool(b)` does exactly the same thing. – Martijn Pieters Nov 03 '14 at 12:48
  • @MartijnPieters The `^` operator calls `__xor__` internally. – Quantum7 Feb 15 '18 at 08:41
  • 7
    @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 that `bool(a) ^ bool(b)` works fine, there is no need to use the `operator.xor()` function here. – Martijn Pieters Feb 15 '18 at 14:21
  • ^ 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++. – alan2here Apr 14 '21 at 13:43
  • @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? – Edward Spencer Sep 22 '21 at 09:31
  • @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 that `operator.xor()` was the only way to do this. – Martijn Pieters Sep 22 '21 at 10:26
  • 1
    This is more readable than `^`. – ijoseph Sep 07 '22 at 21:26
55

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".
Community
  • 1
  • 1
ddaa
  • 52,890
  • 7
  • 50
  • 59
  • 12
    So, 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"? – tzot Jan 11 '09 at 22:59
  • @ΤΖΩΤΖΙΟΥ: Wrap it in a `bool()` – BlueRaja - Danny Pflughoeft Apr 20 '10 at 13:26
  • 4
    @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 Oct 23 '12 at 21:18
  • 9
    Also, 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. – ely Oct 23 '12 at 21:20
  • 2
    @EMS: thanks. Your objections clarified (to me) what ddaa meant. – tzot Oct 24 '12 at 21:57
  • 3
    @Mr.F Your explanation doesn't really excuse this answer. In Python, if you just do `True + True + False + True`, you *do* get `3`, and `True + True + False + True == 3` gives back `True` while `True + True + False + True == 1` gives back `False`. In other words, the answer here doesn't generalize correctly; for it to do so, you need to do additional work. Meanwhile a simple `True ^ True ^ False ^ True` works as expected. – jpmc26 Aug 13 '15 at 21:18
  • 5
    @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 in `True + True + False + True` you should expect the result to be `False` since more than one of those is `True`, which works if the condition checks for `== 1`. – ely Aug 14 '15 at 00:40
  • 2
    So when you write `True + True + False + True == 1` gives back `False` 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". – ely Aug 14 '15 at 00:41
  • (bool(a) + bool(b) + bool(c)) % 2 == 1 would work but it's not worth `^`-ing. – minmaxavg Jan 03 '16 at 12:09
  • Does 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 Nov 27 '17 at 22:38
  • @ddaa I don't see why. It is a crucial consideration for your first proposed answer. – Alan Nov 28 '17 at 15:51
  • @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`. – Alan Nov 28 '17 at 18:02
  • You 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. – ddaa Nov 30 '17 at 13:15
  • Indeed there is. That's why you can run the same python program on multiple differently implemented interpreters – DylanYoung Mar 09 '20 at 21:06
  • 1
    Addition to get XOR obtuse and unclear, violating PEP 20 "Explicit is better than implicit. Simple is better than complex.", downvoting – Ed Griebel Jan 31 '22 at 17:03
34
  • Python logical or: A or B: returns A if bool(A) is True, otherwise returns B
  • Python logical and: A and B: returns A if bool(A) is False, otherwise returns B

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'
nosklo
  • 217,122
  • 57
  • 293
  • 297
  • 5
    This seems bad, or at least weird, to me. None of the other built-in logical operators return one of three possible values. – Zach Hirsch Jan 11 '09 at 19:12
  • 2
    @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 – nosklo Nov 25 '09 at 22:52
  • Logical operation must return logical value, so second "return a or b" looks strange, so second return must return True. – Denis Barmenkov Feb 10 '11 at 13:36
  • 10
    @Denis Barmenkov: Well, note that python logical operators `and` and `or` won't return logical value. `'foo' and 'bar'` returns `'bar'` ... – nosklo Feb 13 '11 at 02:11
  • 8
    At 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-in `and` and `or`. However, of course, in practical situations, `bool(a) ^ bool(b)` or even `a ^ b` (if `a` and `b` are known to be `bool`) are more concise of course. – Erik Kaplun Jan 13 '12 at 13:48
32

I've tested several approaches (including using the truth() function as ShadowRanger suggested).

%timeit  (not a) ^  (not b)   # 47 ns
%timeit  (not a) != (not b)   # 44.7 ns
%timeit truth(a) != truth(b)  # 116 ns
%timeit  bool(a) != bool(b)   # 190 ns
Michael Currie
  • 13,721
  • 9
  • 42
  • 58
Rugnar
  • 2,894
  • 3
  • 25
  • 29
  • 15
    That's 100 ns of my life I won't get back ;-) – Arel Jul 25 '19 at 14:13
  • 6
    For an in-between timing, you can do `from operator import truth` at the top of the module, and test `truth(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 the `tuple` and `dict` to extract them), where `truth` (being a function) can use an optimized path that doesn't require either a `tuple` or a `dict`, and runs in about half the time of `bool` based solutions (but still longer than `not` based solutions). – ShadowRanger Sep 26 '19 at 18:19
  • Which version of which Python implementation does this refer to? – Lutz Prechelt Jul 22 '21 at 07:26
  • @LutzPrechelt unfortunately I don't remember; Probably 3.5 – Rugnar Jul 24 '21 at 21:18
  • Should note your timings are python 3. Python 2.7 timings match for `not` and `bool`, but `truth (a) != truth (b)` is about 50ns instead of 100+. So on par with `not` methods. – Ed_ Jul 27 '23 at 15:41
  • `(not a) is not (not b)` is faster than `(not a) != (not b)`. – benrg Aug 26 '23 at 23:05
28

Python has a bitwise exclusive-OR operator, it's ^:

>>> True ^ False
True
>>> True ^ True
False
>>> False ^ True
True
>>> False ^ False
False

You can use it by converting the inputs to booleans before applying xor (^):

bool(a) ^ bool(b)

(Edited - thanks Arel)

Tomer Gal
  • 933
  • 12
  • 21
  • Your answer should make clear that `^` is a *bitwise* xor (not logical xor like the question asked). `bool(2) ^ bool(3)` gives a different answer than `bool(2 ^ 3)`. – Arel Jul 18 '19 at 14:08
  • 2
    @Arel But that's not the case. `a ^ b` is polymorph. If `a` and `b` are `bool` instances, the result will be `bool` as well. This behavior can hardly be called a "bitwise" xor. – Alfe Aug 15 '19 at 10:19
  • @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 for `bool` and `int` types. Note: `True ^ 2` is `3`, demonstrating how it is indeed bitwise. – Arel Aug 17 '19 at 06:03
  • 1
    @Arel Yes, the `bool ^ int` case is kind of casting everything to `int` first. Still, Python has built-in the `^` operator for many bits in `int` and for the one bit represented in a `bool`, so both are _bitwise_, but the _bitwise_ xor for a single bit just _is_ the _logical_ xor for booleans. – Alfe Aug 19 '19 at 11:52
  • I 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, ie `2^3 = pow(2,3)` which means I always explicitly comment to prevent confusion. – Nicholas Hamilton Sep 08 '19 at 23:52
17

Simple, easy to understand:

sum(bool(a), bool(b)) == 1

If an exclusive choice is what you're after, i.e. to select 1 choice out of n, it can be expanded to multiple arguments:

sum(bool(x) for x in y) == 1
Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
c z
  • 7,726
  • 3
  • 46
  • 59
13

To get the logical xor of two or more variables in Python:

  1. Convert inputs to booleans
  2. Use the bitwise xor operator (^ or operator.xor)

For example,

bool(a) ^ bool(b)

When you convert the inputs to booleans, bitwise xor becomes logical xor.

Note that the accepted answer is wrong: != is not the same as xor in Python because of the subtlety of operator chaining.

For instance, the xor of the three values below is wrong when using !=:

True ^  False ^  False  # True, as expected of XOR
True != False != False  # False! Equivalent to `(True != False) and (False != False)`

(P.S. I tried editing the accepted answer to include this warning, but my change was rejected.)

Arel
  • 1,339
  • 17
  • 22
13

Given that A and B are bools.

A is not B
alan2here
  • 3,223
  • 6
  • 37
  • 62
12

As I don't see the simple variant of xor using variable arguments and only operation on Truth values True or False, I'll just throw it here for anyone to use. It's as noted by others, pretty (not to say very) straightforward.

def xor(*vars):
    result = False
    for v in vars:
        result = result ^ bool(v)
    return result

And usage is straightforward as well:

if xor(False, False, True, False):
    print "Hello World!"

As this is the generalized n-ary logical XOR, it's truth value will be True whenever the number of True operands is odd (and not only when exactly one is True, this is just one case in which n-ary XOR is True).

Thus if you are in search of a n-ary predicate that is only True when exactly one of it's operands is, you might want to use:

def isOne(*vars):
    result = False
    for v in vars:
        if result and v:
            return False
        else:
            result = result or v
    return result
Denis Barmenkov
  • 2,276
  • 1
  • 15
  • 20
micdah
  • 364
  • 2
  • 5
10

Rewarding thread:

Anoder idea... Just you try the (may be) pythonic expression «is not» in order to get behavior of logical «xor»

The truth table would be:

>>> True is not True
False
>>> True is not False
True
>>> False is not True
True
>>> False is not False
False
>>>

And for your example string:

>>> "abc" is not  ""
True
>>> 'abc' is not 'abc' 
False
>>> 'abc' is not '' 
True
>>> '' is not 'abc' 
True
>>> '' is not '' 
False
>>> 

However; as they indicated above, it depends of the actual behavior you want to pull out about any couple strings, because strings aren't boleans... and even more: if you «Dive Into Python» you will find «The Peculiar Nature of "and" and "or"» http://www.diveintopython.net/power_of_introspection/and_or.html

Sorry my writed English, it's not my born language.

Regards.

Agustin Marcos
  • 101
  • 1
  • 2
  • I 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 Mar 28 '19 at 17:23
  • I 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" – yucer Mar 28 '19 at 17:24
9

You use the same XOR operator like in C, which is ^.

I don't know why, but the most upvoted solution suggests bool(A) != bool(B), while I would say - in conformity with C's ^'s operator, the most obvious solution is:

bool(A) ^ bool(B)

which is more readable and immediately understandable for anyone coming from C or any C-derived language ...

when doing code-golfing, probably

not A ^ (not B)

will be the winner. with not as converter for boolean (one letter less than bool(). And for the first expression in some cases one can leave out the parantheses. Well, it depends, in cases where one has to do not(A) ^ (not(B)), the bool() needs same amount of letters ...

Gwang-Jin Kim
  • 9,303
  • 17
  • 30
8

I know this is late, but I had a thought and it might be worth, just for documentation. Perhaps this would work:np.abs(x-y) The idea is that

  1. if x=True=1 and y=False=0 then the result would be |1-0|=1=True
  2. if x=False=0 and y=False=0 then the result would be |0-0|=0=False
  3. if x=True=1 and y=True=1 then the result would be |1-1|=0=False
  4. if x=False=0 and y=True=1 then the result would be |0-1|=1=True
  • You could even drop the `abs`, python interprets negative numbers as truthy, although this is very obscure imo (what does `if (x > 1) - (y > 3)` mean? – Nearoo Oct 03 '20 at 14:04
8

Many folks, including myself, need an xor function that behaves like an n-input xor circuit, where n is variable. (See https://en.wikipedia.org/wiki/XOR_gate). The following simple function implements this.

def xor(*args):
   """
   This function accepts an arbitrary number of input arguments, returning True
   if and only if bool() evaluates to True for an odd number of the input arguments.
   """

   return bool(sum(map(bool,args)) % 2)

Sample I/O follows:

In [1]: xor(False, True)
Out[1]: True

In [2]: xor(True, True)
Out[2]: False

In [3]: xor(True, True, True)
Out[3]: True
Armali
  • 18,255
  • 14
  • 57
  • 171
Phillip M. Feldman
  • 468
  • 1
  • 7
  • 17
  • I usually prefer generator comprehensions over using `map`, since they are usually faster and (for me personally) generally more explicit, so I'd use `bool(sum(bool(a) for a in args) % 2)`. On the other hand, I think `map` is slightly faster with built-in functions (like `bool`). A good solution anyways +1. – Alex Potapenko Dec 08 '22 at 16:38
7

Some of the implementations suggested here will cause repeated evaluation of the operands in some cases, which may lead to unintended side effects and therefore must be avoided.

That said, a xor implementation that returns either True or False is fairly simple; one that returns one of the operands, if possible, is much trickier, because no consensus exists as to which operand should be the chosen one, especially when there are more than two operands. For instance, should xor(None, -1, [], True) return None, [] or False? I bet each answer appears to some people as the most intuitive one.

For either the True- or the False-result, there are as many as five possible choices: return first operand (if it matches end result in value, else boolean), return first match (if at least one exists, else boolean), return last operand (if ... else ...), return last match (if ... else ...), or always return boolean. Altogether, that's 5 ** 2 = 25 flavors of xor.

def xor(*operands, falsechoice = -2, truechoice = -2):
  """A single-evaluation, multi-operand, full-choice xor implementation
  falsechoice, truechoice: 0 = always bool, +/-1 = first/last operand, +/-2 = first/last match"""
  if not operands:
    raise TypeError('at least one operand expected')
  choices = [falsechoice, truechoice]
  matches = {}
  result = False
  first = True
  value = choice = None
  # avoid using index or slice since operands may be an infinite iterator
  for operand in operands:
    # evaluate each operand once only so as to avoid unintended side effects
    value = bool(operand)
    # the actual xor operation
    result ^= value
    # choice for the current operand, which may or may not match end result
    choice = choices[value]
    # if choice is last match;
    # or last operand and the current operand, in case it is last, matches result;
    # or first operand and the current operand is indeed first;
    # or first match and there hasn't been a match so far
    if choice < -1 or (choice == -1 and value == result) or (choice == 1 and first) or (choice > 1 and value not in matches):
      # store the current operand
      matches[value] = operand
    # next operand will no longer be first
    first = False
  # if choice for result is last operand, but they mismatch
  if (choices[result] == -1) and (result != value):
    return result
  else:
    # return the stored matching operand, if existing, else result as bool
    return matches.get(result, result)

testcases = [
  (-1, None, True, {None: None}, [], 'a'),
  (None, -1, {None: None}, 'a', []),
  (None, -1, True, {None: None}, 'a', []),
  (-1, None, {None: None}, [], 'a')]
choices = {-2: 'last match', -1: 'last operand', 0: 'always bool', 1: 'first operand', 2: 'first match'}
for c in testcases:
  print(c)
  for f in sorted(choices.keys()):
    for t in sorted(choices.keys()):
      x = xor(*c, falsechoice = f, truechoice = t)
      print('f: %d (%s)\tt: %d (%s)\tx: %s' % (f, choices[f], t, choices[t], x))
  print()
7

Sometimes I find myself working with 1 and 0 instead of boolean True and False values. In this case xor can be defined as

z = (x + y) % 2

which has the following truth table:

     x
   |0|1|
  -+-+-+
  0|0|1|
y -+-+-+
  1|1|0|
  -+-+-+
Steve L
  • 1,523
  • 3
  • 17
  • 24
7

Exclusive Or is defined as follows

def xor( a, b ):
    return (a or b) and not (a and b)
S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • 2
    that would return True for xor('this', '') and to follow python's way, it should return 'this'. – nosklo Jan 11 '09 at 13:45
  • @nosklo: Take it up with the BDFL, please, not me. Since Python returns True, then that *must* be Python's way. – S.Lott Jan 11 '09 at 14:15
  • 2
    I 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. – nosklo Jan 11 '09 at 15:09
  • 10
    Python `and` and `or` do short-circuit. Any `xor` implementation can't short-circuit, so there is already a discrepancy; therefore, there is no reason that `xor` should operate like `and`+`or` do. – tzot Jan 11 '09 at 23:12
6

How about this?

(not b and a) or (not a and b)

will give a if b is false
will give b if a is false
will give False otherwise

Or with the Python 2.5+ ternary expression:

(False if a else b) if b else a
Markus Jarderot
  • 86,735
  • 21
  • 136
  • 138
6

Xor is ^ in Python. It returns :

  • A bitwise xor for ints
  • Logical xor for bools
  • An exclusive union for sets
  • User-defined results for classes that implements __xor__.
  • TypeError for undefined types, such as strings or dictionaries.

If you intend to use them on strings anyway, casting them in bool makes your operation unambiguous (you could also mean set(str1) ^ set(str2)).

Diane M
  • 1,503
  • 1
  • 12
  • 23
6

This is how I would code up any truth table. For xor in particular we have:

| a | b  | xor   |             |
|---|----|-------|-------------|
| T | T  | F     |             |
| T | F  | T     | a and not b |
| F | T  | T     | not a and b |
| F | F  | F     |             |

Just look at the T values in the answer column and string together all true cases with logical or. So, this truth table may be produced in case 2 or 3. Hence,

xor = lambda a, b: (a and not b) or (not a and b)
snagpaul
  • 348
  • 3
  • 6
4

This gets the logical exclusive XOR for two (or more) variables

str1 = raw_input("Enter string one:")
str2 = raw_input("Enter string two:")

any([str1, str2]) and not all([str1, str2])

The first problem with this setup is that it most likely traverses the whole list twice and, at a minimum, will check at least one of the elements twice. So it may increase code comprehension, but it doesn't lend to speed (which may differ negligibly depending on your use case).

The second problem with this setup is that it checks for exclusivity regardless of the number of variables. This is may at first be regarded as a feature, but the first problem becomes a lot more significant as the number of variables increases (if they ever do).

Marc
  • 4,820
  • 3
  • 38
  • 36
4

It's easy when you know what XOR does:

def logical_xor(a, b):
    return (a and not b) or (not a and b)

test_data = [
  [False, False],
  [False, True],
  [True, False],
  [True, True],
]

for a, b in test_data:
    print '%r xor %s = %r' % (a, b, logical_xor(a, b))
Denis Barmenkov
  • 2,276
  • 1
  • 15
  • 20
3

XOR is implemented in operator.xor.

lbolla
  • 5,387
  • 1
  • 22
  • 35
  • 8
    operator.xor corresponds to the bitwise operation, which it the one that the original poster doesn't want. – Niriel May 07 '12 at 12:30
  • @kojiro [evidently so!](https://stackoverflow.com/questions/432842/how-do-you-get-the-logical-xor-of-two-variables-in-python/57203199#comment27750647_11036506) – Arel Jul 25 '19 at 14:16
2

Here's an implementation of the map-reduce generalization. Note that this is equivalent to functools.reduce(lambda x, y: x != y, map(bool, orands)).

def xor(*orands):
    return bool(sum(bool(x) for x in orands) % 2)

Here's a generalization if you're looking for a one-hot detector. This generalization may fit the English language use of exclusive-or, (e.g. "For a dollar you can buy a juice or coffee or tea"), but it doesn't match the typical order of operations. E.g.xor_1hot(1,1,1) == 0 != 1 == xor_1hot(xor_1hot(1,1),1).

def xor_1hot(*orands):
    return sum(bool(x) for x in orands) == 1

You can test either with

# test
from itertools import product
n = 3
total_true = 0
for inputs in product((False, True), repeat=n):
    y = xor(*inputs)
    total_true += int(y)
    print(f"{''.join(str(int(b)) for b in inputs)}|{y}")
print('Total True:', total_true)

One-Hot Detector Output:

000|False
001|True
010|True
011|False
100|True
101|False
110|False
111|False
Total True: 3

Map-Reduce Output:

000|False
001|True
010|True
011|False
100|True
101|False
110|False
111|True
Total True: 4

mathandy
  • 1,892
  • 25
  • 32
  • You handle 3+ orands case wrong: `1 XOR 1 XOR 1` should be `True`, not `111|False`. This is easy to fix: just add `% 2` of the sum to the function. However, this makes it almost identical to @Phillip M. Feldman's answer. – Alex Potapenko Dec 08 '22 at 16:47
  • @AlexPotapenko yeah, you're right. I've added my new understanding to the answer. – mathandy Dec 18 '22 at 16:04
  • 1
    You now probably have a small typo: it should be `bool(sum(bool(x) for x in orands) % 2)`, otherwise it is always `True` except for all `False` orands due to all non-zero sums casting to `True`. Your previous `== 1` approach is also perfectly valid: `sum(bool(x) for x in orands) % 2 == 1` – Alex Potapenko Dec 23 '22 at 13:08
1

Just because I haven't seen it mentioned elsewhere, this also does the trick:

def logical_xor(a, b):
    return not b if a else bool(b)

I'm not sure if it's "better"/more readable/more pythonic than the accepted solution bool(a) != bool(b).

GdV
  • 21
  • 4
0

The way that Python handles logic operations can be confusing, so my implementation gives the user the option (by default) of a simple True/False answer. The actual Python result can be obtained by setting the optional third arg to None.

def xor(a, b, true=True, false=False): # set true to None to get actual Python result
    ab1 = a and not b
    ab2 = not a and b
    if bool(ab1) != bool(ab2):
        return (ab1 or ab2) if true is None else true
    else:
        return false
GaryMBloom
  • 5,350
  • 1
  • 24
  • 32
-2

I did a bit of testing and this seems to be a good solution in terms of efficiency and flexibility. It makes use of efficient builtins and does not perform unnecessary operations - stops early if condition failed. It tests for truth value.

I found one solution, which performs better on early stop, but it is very inefficient on long iterables.

def xor(values, n=1):
    filt = filter(None, values)
    for i in range(n + 1):
        if next(filt, None) is None:
            return i == n
    return i + 1 == n
dgrigonis
  • 387
  • 5
  • 10
-11

We can easily find xor of two variables by the using:

def xor(a,b):
    return a !=b

Example:

xor(True,False) >>> True

Muhammad Abdullah
  • 305
  • 1
  • 2
  • 10