How do you get the logical xor of two variables in Python?

1,040,656

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: 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'
Share:
1,040,656
Zach Hirsch
Author by

Zach Hirsch

Updated on April 30, 2022

Comments

  • Zach Hirsch
    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
      mmx over 15 years
      How do you define "xor" for a couple of strings? What do you feel "abc" ^ "" should return that it doesn't?
    • Zach Hirsch
      Zach Hirsch over 15 years
      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.
    • Wowbagger and his liquid lunch
      Wowbagger and his liquid lunch over 11 years
      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.
    • Kaz
      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 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.
    • mckenzm
      mckenzm about 9 years
      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.
    • Guimoute
      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 call hex on the result, then pad the result with enough 0 on the left to reach the longest length.
    • mckenzm
      mckenzm over 2 years
      @Guimoute Thanks, a bit of work involved, but doable with filling/alignment.
  • nosklo
    nosklo over 15 years
    that would return True for xor('this', '') and to follow python's way, it should return 'this'.
  • user1066101
    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
    nosklo over 15 years
    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.
  • Zach Hirsch
    Zach Hirsch over 15 years
    This seems bad, or at least weird, to me. None of the other built-in logical operators return one of three possible values.
  • Ishbir
    Ishbir over 15 years
    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"?
  • Ishbir
    Ishbir over 15 years
    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.
  • SingleNegationElimination
    SingleNegationElimination almost 15 years
    great post, but of all ways to name your parameters, why 'str1' and 'str2'?
  • nosklo
    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
    orokusaki over 14 years
    @Token why not. Do you mean because they aren't very Pythonic?
  • SingleNegationElimination
    SingleNegationElimination over 14 years
    @orokusaki: because they don't seem to suggest their intended use very well.
  • orokusaki
    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
    BlueRaja - Danny Pflughoeft about 14 years
    @ΤΖΩΤΖΙΟΥ: Wrap it in a bool()
  • Denis Barmenkov
    Denis Barmenkov about 13 years
    Logical operation must return logical value, so second "return a or b" looks strange, so second return must return True.
  • nosklo
    nosklo about 13 years
    @Denis Barmenkov: Well, note that python logical operators and and or won't return logical value. 'foo' and 'bar' returns 'bar' ...
  • rjmunro
    rjmunro almost 13 years
    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.
  • Erik Kaplun
    Erik Kaplun over 12 years
    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.
  • Admin
    Admin about 12 years
    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.
  • Niriel
    Niriel about 12 years
    operator.xor corresponds to the bitwise operation, which it the one that the original poster doesn't want.
  • AmigoNico
    AmigoNico almost 12 years
    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!
  • user541686
    user541686 almost 12 years
    @TokenMacGuy: What were you suggesting he should name them instead?
  • ril3y
    ril3y almost 12 years
    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'
  • elmo
    elmo almost 12 years
    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".
  • ely
    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
    ely over 11 years
    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.
  • Ishbir
    Ishbir over 11 years
    @EMS: thanks. Your objections clarified (to me) what ddaa meant.
  • Wowbagger and his liquid lunch
    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
    askewchan over 10 years
    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.
  • Martijn Pieters
    Martijn Pieters over 9 years
    @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.
  • pathunstrom
    pathunstrom about 9 years
    For improving this answer: (bool(False) is False) == True. You can just use False on those lines.
  • jpmc26
    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 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.
  • ely
    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 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
    ely over 8 years
    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".
  • minmaxavg
    minmaxavg over 8 years
    (bool(a) + bool(b) + bool(c)) % 2 == 1 would work but it's not worth ^-ing.
  • mrucci
    mrucci over 7 years
    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!
  • RNA
    RNA about 7 years
    should it be bool(a) is not bool(b) instead?
  • Alan
    Alan over 6 years
    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
    Alan over 6 years
    @ddaa I don't see why. It is a crucial consideration for your first proposed answer.
  • Alan
    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
    ddaa over 6 years
    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.
  • Quantum7
    Quantum7 about 6 years
    @MartijnPieters The ^ operator calls __xor__ internally.
  • Martijn Pieters
    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 that bool(a) ^ bool(b) works fine, there is no need to use the operator.xor() function here.
  • Jason
    Jason about 6 years
    Is this the real answer? I try this code on my machine, print bool("Hellow World") == bool("H"), it returns True
  • DylanYoung
    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
    Mayou36 over 5 years
    or xor("hey", "there") >>> True, but that's not what we want
  • ɹɐʎɯɐʞ
    ɹɐʎɯɐʞ about 5 years
    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 would be a better alternative.
  • yucer
    yucer about 5 years
    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
    yucer about 5 years
    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"
  • jpmc26
    jpmc26 almost 5 years
    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.
  • Arel
    Arel almost 5 years
    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
    Arel almost 5 years
    Downvoting because this answer is WRONG (since as pointed out by @elmo != is not XOR in python)
  • Arel
    Arel almost 5 years
    That's 100 ns of my life I won't get back ;-)
  • Arel
    Arel almost 5 years
    @kojiro evidently so!
  • Alfe
    Alfe over 4 years
    @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.
  • Arel
    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 for bool and int types. Note: True ^ 2 is 3, demonstrating how it is indeed bitwise.
  • Alfe
    Alfe over 4 years
    @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.
  • warvariuc
    warvariuc over 4 years
    sum(map(bool, y)) % 2 == 1
  • Nicholas Hamilton
    Nicholas Hamilton over 4 years
    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.
  • ShadowRanger
    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
    ShadowRanger over 4 years
    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).
  • Arel
    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
    DylanYoung about 4 years
    Why blame the respondent for poor reviewers?
  • DylanYoung
    DylanYoung about 4 years
    Indeed there is. That's why you can run the same python program on multiple differently implemented interpreters
  • wjandrea
    wjandrea almost 4 years
  • Josiah Yoder
    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
    wjandrea almost 4 years
    @Josiah Yes, but we can't be sure that True is True because that's an implementation detail.
  • Josiah Yoder
    Josiah Yoder almost 4 years
  • wjandrea
    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
    Nearoo over 3 years
    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?
  • Boris Verkhovskiy
    Boris Verkhovskiy about 3 years
    I see little reason to use sum if you just have 2 variables, bool(a) + bool(b) == 1 does the same thing.
  • Guimoute
    Guimoute about 3 years
    Your answer bool(a) ^ bool(b) shows much more clearly the xor intent of the programmer than the currently accepted answer of bool(a) != bool(b).
  • alan2here
    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
    Lutz Prechelt almost 3 years
    Which version of which Python implementation does this refer to?
  • Rugnar
    Rugnar almost 3 years
    @LutzPrechelt unfortunately I don't remember; Probably 3.5
  • c z
    c z over 2 years
    @Boris Potayto potarto
  • Boris Verkhovskiy
    Boris Verkhovskiy over 2 years
    @cz I'm glad that you agree that one is clearly wrong :)
  • JJL
    JJL over 2 years
    For a single comparison, this construction has the advantage of interpreting None as False whereas ^ on None throws a TypeError
  • Edward Spencer
    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
    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 that operator.xor() was the only way to do this.
  • Clément
    Clément over 2 years
    not not A is another way to get the same result as bool(A) without a function call.
  • Ed Griebel
    Ed Griebel over 2 years
    Addition to get XOR obtuse and unclear, violating PEP 20 "Explicit is better than implicit. Simple is better than complex.", downvoting
  • Vladimir Vilimaitis
    Vladimir Vilimaitis about 2 years
    This is pretty clever, I like it.