Zach Hirsch
Zach Hirsch

Reputation: 26121

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

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'

Upvotes: 1014

Views: 1395521

Answers (29)

Denis Barmenkov
Denis Barmenkov

Reputation: 2309

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)))

Upvotes: 5

alan2here
alan2here

Reputation: 3327

xor = ((False, True), (True, False)
eq = (xor[1], xor[0])

xor[False][True] # yields True

advantages:

explicit

makes operations on logic gates available

can frame as your choice of (False, True), (0, 1), and the such

can use lists rather than tuples if desired

.

disadvantages:

non-standard

subjectively goofy

plausibly less performant than other approaches

Upvotes: 0

Marc
Marc

Reputation: 5516

The meaning of Exclusive Or (XOR) can be confusing but may have lead you here.

Here is an exclusivity check that functions like the traditional XOR for two variables but operates like One-Hot for multiple variables.

The only benefit I can see above other implementations is that it short circuits and is less magical.

    def exclusive(*opts):
        count = 0
        for value in opts:
            if bool(value):  # If truthy
                count += 1
                if count > 1:  # Short-circuit
                    return False
        return count == 1  # Only Return True if one truthy found

Here's a table of those differences between XOR and ONE-HOT as an example:

a|b|c|d --- xor|one-hot
-----------------------
0|0|0|0 --> 0  |0  
0|0|0|1 --> 1  |1  
0|0|1|0 --> 1  |1  
0|0|1|1 --> 0  |0  
0|1|0|0 --> 1  |1  
0|1|0|1 --> 0  |0  
0|1|1|0 --> 0  |0  
0|1|1|1 --> 1  |0  # noted difference
1|0|0|0 --> 1  |1  
1|0|0|1 --> 0  |0  
1|0|1|0 --> 0  |0  
1|0|1|1 --> 1  |0  # noted difference
1|1|0|0 --> 0  |0  
1|1|0|1 --> 1  |0  # noted difference
1|1|1|0 --> 1  |0  # noted difference
1|1|1|1 --> 0  |0

Upvotes: 5

singingwolfboy
singingwolfboy

Reputation: 5556

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
IMPORTANT NOTE

"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, 2013 at 16:59 "

Upvotes: 255

Rugnar
Rugnar

Reputation: 3132

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

Upvotes: 34

mathandy
mathandy

Reputation: 2010

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

Upvotes: 2

A. Coady
A. Coady

Reputation: 57408

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

bool(a) != bool(b)

Upvotes: 1735

GdV
GdV

Reputation: 41

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).

Upvotes: 2

Gwang-Jin Kim
Gwang-Jin Kim

Reputation: 9990

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 ...

Upvotes: 11

alan2here
alan2here

Reputation: 3327

Given that A and B are bools.

A is not B

Upvotes: 12

c z
c z

Reputation: 9027

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

Upvotes: 17

micdah
micdah

Reputation: 364

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

Upvotes: 12

GaryMBloom
GaryMBloom

Reputation: 5690

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

Upvotes: -1

snagpaul
snagpaul

Reputation: 358

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)

Upvotes: 6

Tomer Gal
Tomer Gal

Reputation: 969

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)

Upvotes: 31

Arel
Arel

Reputation: 1461

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.)

Upvotes: 15

Phillip M. Feldman
Phillip M. Feldman

Reputation: 546

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

Upvotes: 9

Diane M
Diane M

Reputation: 1512

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)).

Upvotes: 6

Muhammad Abdullah
Muhammad Abdullah

Reputation: 301

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

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

Example:

xor(True,False) >>> True

Upvotes: -13

BarocliniCplusplus
BarocliniCplusplus

Reputation: 283

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

Upvotes: 8

Agustin Marcos
Agustin Marcos

Reputation: 109

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.

Upvotes: 10

Steve L
Steve L

Reputation: 1645

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|
  -+-+-+

Upvotes: 7

lbolla
lbolla

Reputation: 5411

XOR is implemented in operator.xor.

Upvotes: 0

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()

Upvotes: 7

ddaa
ddaa

Reputation: 54514

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".

Upvotes: 58

Markus Jarderot
Markus Jarderot

Reputation: 89221

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

Upvotes: 6

S.Lott
S.Lott

Reputation: 391972

Exclusive Or is defined as follows

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

Upvotes: 7

nosklo
nosklo

Reputation: 223102

  • 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'

Upvotes: 36

Zach Hirsch
Zach Hirsch

Reputation: 26121

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.

Upvotes: 716

Related Questions