如何在Python中获得两个变量的逻辑异或?


647

如何在Python中获得两个变量的逻辑异或?

例如,我有两个期望是字符串的变量。我想测试其中只有一个包含True值(不是None或空字符串):

str1 = raw_input("Enter string one:")
str2 = raw_input("Enter string two:")
if logical_xor(str1, str2):
    print "ok"
else:
    print "bad"

^运营商似乎是按位,并在所有对象没有定义:

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

3
如何为几个字符串定义“异或”?您觉得“ abc” ^“”应该返回不是吗?
Mehrdad Afshari,2009年

18
它应该返回True,而不是引发异常,因为只有一个字符串是True(由普通Python的bool类型定义)。
Zach Hirsch,2009年

38
我很惊讶Python没有一个名为“ xor”的中缀运算符,这将是最直观的Pythonic实现。使用“ ^”与其他语言一致,但是不像大多数Python那样公然可读。
Mark E. Haase 2012年

13
@MehrdadAfshari对于您的问题,a xor a很明显的答案是定义为(a and not b) or (not a and b),因此a xor b,当ab是字符串或任何其他类型时,应该产生任何(a and not b) or (not a and b)收益。
卡兹(Kaz)

1
问题是文档很差。^是“按位异或”,按字面意思解释是一点一点而不是逐个布尔。因此x'FFFF00'^ x'FFFF00'应该为x'000000'。还是这仅意味着逐个字符地进行?转换为数字?我们需要迭代较短的字符串字符以匹配较长的字符串的长度。这一切都应该是内置的。
mckenzm

Answers:


1187

如果您已经将输入归一化为布尔值,则!=为xor。

bool(a) != bool(b)

148
尽管这很聪明而且很简短,但我不认为这很干净。当有人在代码中读取此构造时,对他们来说这是xor操作立即显而易见吗?我不得不添加评论-对我来说,我正在编写不清楚的代码并尝试以评论道歉。

47
也许“很明显这是XOR?” 是错误的问题。我们只是想看看两个问题的答案是否相同,并认为我们将使用XOR来实现。例如,如果我们要确保不将苹果与橙子进行比较,“ if xor(isApple(x),isApple(y))”是否真的比“ if isApple(x)!= isApple(y)”更清晰?不对我!
AmigoNico 2012年

106
使用“!=”作为异或有问题。您可能希望bool(a)!= bool(b)!= bool(c)与bool(a)^ bool(b)^ bool(c)相同。转换为布尔也是如此,但我建议使用^。要了解第一个示例中发生了什么,请查找“操作员链接”。
elmo

19
@elmo:+1指出区别,+1告诉我什么算子链接!我在营地里说!=不如^可读。
Mark E. Haase 2012年

13
应该bool(a) is not bool(b)代替吗?
RNA

485

您始终可以使用xor的定义从其他逻辑运算中进行计算:

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

但这对我来说太冗长了,乍一看并不清楚。另一种方法是:

bool(a) ^ bool(b)

两个布尔值的xor运算符是逻辑xor(与ints不同,在ints上是按位的)。这是有道理的,因为bool它只是的子类int,但是被实现为仅具有01。当域限制为0和时,逻辑异或等效于按位异或1

因此,该logical_xor功能将实现为:

def logical_xor(str1, str2):
    return bool(str1) ^ bool(str2)

感谢尼克·科格伦了Python-3000的邮件列表上


7
很棒的文章,但是在所有命名参数的方式中,为什么要使用“ str1”和“ str2”?
SingleNegationElimination 2009年

1
@Token为什么不呢?您是说不是因为它们不是Python语言吗?
orokusaki 2010年

1
@Zach Hirsch您是否可以使用(而不是a和b)而不是(b和非a)来提高可读性,或者该定义与xor不一致。
orokusaki'2

10
您应该像这样将not放在首位,(not b and a) or (not a and b)以便在有字符串的情况下返回字符串,这似乎是函数运行的pythonic方式。
rjmunro 2011年

2
@TokenMacGuy:您建议他改用什么名字呢?
user541686 2012年

180

operator模块(与^运算符相同)中,Python内置了按位异或运算符:

from operator import xor
xor(bool(a), bool(b))  # Note: converting to bools is essential

3
这就是我所需要的。当进行逆向工程恶意软件时,很多时候字符串会被扭曲,直到进行XOR操作为止。使用该字符(XOR(ORD( “N”),0x1A的))= 'T'
ril3y

75
注意,这也是按位的:xor(1, 2)return 3。从文档字符串: xor(a, b) -- Same as a ^ b. 请记住,从中导入的任何内容operator都只是现有内置infix运算符的功能形式。
askewchan

5
@askewchan:该bool类型重载__xor__以返回布尔值。它会很好地工作,但在bool(a) ^ bool(b)完全相同的情况下会显得过分杀伤力。
马丁·彼得斯

@MartijnPieters接线员在内部进行^呼叫__xor__
Quantum7

5
@ Quantum7:是的,我不确定您为什么要告诉我这一点。我只是说bool类型专门因为调用它而实现了该__xor__方法。可以正常工作的地方是,这里不需要使用该功能。^bool(a) ^ bool(b)operator.xor()
马丁·彼得斯

43

正如Zach解释的那样,您可以使用:

xor = bool(a) ^ bool(b)

就个人而言,我赞成略有不同的方言:

xor = bool(a) + bool(b) == 1

该方言的灵感来自我在学校学习的一种逻辑图表语言,其中“ OR”由包含≥1(大于或等于1)的框表示,而“ XOR”由包含的框表示=1

这具有正确实现互斥或在多个操作数上的优势。

  • “ 1 = a ^ b ^ c ...”表示真实操作数的数量为奇数。此运算符是“奇偶校验”。
  • “ 1 = a + b + c ...”表示恰好一个操作数为真。这是“排他性或”,意思是“一个排除其他”。

12
因此,True + True + False + True == 3,并且3!= 1,但是True XOR True XOR False XOR True == True。您能否详细说明“在多个操作数上正确实现XOR”?
tzot

3
@tzot您的示例失败,因为根据ddaa的解决方案,您一次只能将加法应用于两个变量。因此,必须正确地编写所有内容(((((True + True)==1)+False)==1)+True)==1。此处给出的答案完全可以推广到多个操作数。
2012年

6
同样,三路XOR与两个XOR的操作顺序分组组之间也存在差异。所以3-WAY-XOR(A,B,C)是一样的东西XOR(异或(A,B),C)。ddaa的例子是前者,而您的假设是后者。
2012年

3
@ Mr.F您的解释并没有真正成为这个答案的借口。在Python中,如果你只是做True + True + False + True,你得到3,并且True + True + False + True == 3还给True同时True + True + False + True == 1还给False。换句话说,这里的答案不能正确概括。为此,您需要做其他工作。同时,一个简单的True ^ True ^ False ^ True作品符合预期。
jpmc26 2015年

3
@ jpmc26我不明白您的评论。加法旨在概括要检查的一个操作数正好True一个多别名XOR的操作。此操作与例如不同A XOR B XOR ... XOR Z。换句话说,如果您打算使用基于加法的版本,那么在提交操作数后,True + True + False + True您应该期望结果是False其中一个以上的结果True,如果条件检查为,则该结果有效== 1
2015年

26
  • Python的逻辑orA or B:回报A,如果bool(A)True,否则返回B
  • Python的逻辑andA and B:回报A,如果bool(A)False,否则返回B

为了保持大多数思维方式,我的逻辑异或定义为:

def logical_xor(a, b):
    if bool(a) == bool(b):
        return False
    else:
        return a or b

这样,它可以返回abFalse

>>> logical_xor('this', 'that')
False
>>> logical_xor('', '')
False
>>> logical_xor('this', '')
'this'
>>> logical_xor('', 'that')
'that'

5
对我来说,这似乎很糟糕,或者至少很奇怪。其他内置逻辑运算符均未返回三个可能值之一。
Zach Hirsch,2009年

2
@Zach Hirsch:这就是为什么我说“保持大多数思维方式”的原因-因为无论对还是错,都没有良好的结果
nosklo

逻辑运算必须返回逻辑值,因此第二个“返回a或b”看起来很奇怪,因此第二个返回必须返回True。
丹尼斯·巴门科夫

9
@Denis Barmenkov:嗯,注意蟒蛇逻辑运算符andor将不会返回逻辑值。'foo' and 'bar'返回'bar'...
nosklo 2011年

6
乍一看,前面的2个答案似乎是最好的,但经过深思熟虑,这个答案实际上是唯一真正正确的答案,也就是说,它是唯一提供xor与内置and和一致的实现示例的答案or。但是,当然,在实际情况下,bool(a) ^ bool(b)甚至a ^ b(如果ab已知bool)更为简洁。
埃里克·卡普伦

23

我已经测试了几种方法,并且not a != (not b)似乎是最快的。

这是一些测试

%timeit not a != (not b)
10000000 loops, best of 3: 78.5 ns per loop

%timeit bool(a) != bool(b)
1000000 loops, best of 3: 343 ns per loop

%timeit not a ^ (not b)
10000000 loops, best of 3: 131 ns per loop

编辑: 上面的示例1和3缺少括号,因此结果不正确。新结果+ truth()具有ShadowRanger建议的功能。

%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

6
那是我一生的100 ns,我不会回来;-)
Arel

4
对于介于两者之间的计时,您可以from operator import truth在模块顶部进行测试truth(a) != truth(b)bool作为构造函数,在C级别上有很多不可避免的开销(必须接受等效的参数*args, **kwargs并解析tupledict提取它们),其中truth(作为函数)可以使用不需要a tuple或as的优化路径。a dict,其运行时间约为bool基础解决方案的一半(但仍比not基础解决方案要长)。
ShadowRanger

9

奖励线程:

提议者的想法...尝试(可能是)python表达式“不是”以便获得逻辑“异或”的行为

真值表将是:

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

对于您的示例字符串:

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

然而; 正如上面所指出的,这取决于您想对任何几个字符串进行抽出的实际行为,因为字符串不是布尔值……甚至更多:如果您“深入Python”,您会发现“和”和“或”» http://www.diveintopython.net/power_of_introspection/and_or.html

对不起,我写的英语不是我的母语。

问候。


我也习惯将其理解为“严格不同”。这是因为某些语言习惯于用二进制表示形式一点一点地实现该操作,并采用最终的按位运算。我猜你的答案更“防类型验证”,因为它超出了布尔空间。
尤瑟

我的意思是,您的答案涵盖了比较无,错误,''的情况,因为区别在于区别。例如:bool(False)!= bool('')尽管如此,False不是”“更符合“严格不同”的语义
yucer

8

Python具有按位异或运算符,它是^

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

您可以通过在应用xor(^)之前将输入转换为布尔值来使用它:

bool(a) ^ bool(b)

(编辑-感谢Arel)


您的答案应明确说明这^按位异或(不是像所问问题那样的逻辑异或)。bool(2) ^ bool(3)给出的答案与bool(2 ^ 3)
Arel

1
@Arel事实并非如此。 a ^ b是多态的。如果abbool实例,结果也将是bool。这种行为很难被称为“按位”异或。
Alfe

@Alfe重要的一点是必须首先将值强制转换为布尔值。Python文档将其定义^为按位排列,尽管保留类型boolint类型是一个有趣的观点。注意:True ^ 2is 3,说明它确实是按位的。
Arel

@Arel是的,这种bool ^ int情况有点把一切都int放在首位。尽管如此,Python已经内置了^运营商在许多位int,为一个在一位表示bool,所以都是按位,但按位用于单个位异或仅仅逻辑的布尔XOR。
Alfe

我一直讨厌使用此运算符,尽管我了解它是xor来自工程背景,但本能地感觉像是一种数学能力,也就是说2^3 = pow(2,3),我总是明确地评论以防止混淆。
尼古拉斯·汉密尔顿,

8

因为我看不到使用变量参数的xor的简单变体,而仅对True值True或False进行运算,所以我将它扔在这里供任何人使用。正如其他人所指出的那样,很简单(不是很清楚)。

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

使用也很简单:

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

由于这是广义的n元逻辑XOR,因此每当True操作数的数量为奇数时,它的真值将为True(不仅只有当一个为True时,这才是n元XOR为True的一种情况)。

因此,如果您要搜索仅在其中一个操作数正好存在时才为True的n元谓词,则可能需要使用:

def isOne(*vars):
    sum = False
    for v in vars:
        if sum and v:
            return False
        else:
            sum = sum or v
    return sum

为改善此答案:(bool(False) is False) == True。您可以仅False在这些行上使用。
pathunstrom

7

异或定义如下

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

2
对于xor('this','')将返回True并遵循python的方式,它应返回'this'。
09年

@nosklo:请跟BDFL谈谈,不是我。由于Python返回True,所以那一定是Python的方式。
S.Lott,2009年

2
我的意思是与其他python逻辑运算符保持一致-当我这样做时,Python不会返回True(“ this”或“”),而是返回“ this”。但是在函数xor('this','')中返回True。它应该像内置的“或” python一样返回“ this”。
nosklo

10
and和Python or做短路。任何xor实现都不能短路,因此已经存在差异。因此,没有理由xor应该像and+ or做一样工作。
tzot

7

有时我发现自己使用1和0代替布尔True和False值。在这种情况下,xor可以定义为

z = (x + y) % 2

它具有以下真值表:

     x
   |0|1|
  -+-+-+
  0|0|1|
y -+-+-+
  1|1|0|
  -+-+-+

7

我知道这很晚了,但是我有一个想法,可能只是为了文档而已。也许这会起作用:np.abs(x-y)这个想法是

  1. 如果x = True = 1且y = False = 0,则结​​果为| 1-0 | = 1 = True
  2. 如果x = False = 0和y = False = 0,那么结果将是| 0-0 | = 0 = False
  3. 如果x = True = 1和y = True = 1,则结果为| 1-1 | = 0 = False
  4. 如果x = False = 0和y = True = 1,那么结果将是| 0-1 | = 1 = True

7

简单易懂:

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

如果您想要的是排他性选择,则可以将其扩展为多个参数:

sum( bool(x) for x in y ) % 2 == 1

1
sum(map(bool, y)) % 2 == 1
warvariuc

6

这个怎么样?

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

会给a如果b是假的
会给会给b如果a是假的
会给False否则

或使用Python 2.5+三元表达式:

(False if a else b) if b else a

6

在此建议的某些实现在某些情况下将导致对操作数的重复评估,这可能导致意外的副作用,因此必须避免。

这就是说,一个xor实现,无论是收益True还是False相当简单; 如果可能的话,返回一个操作数之一的技巧非常棘手,因为对于选择哪个操作数没有共识,尤其是当有两个以上的操作数时。例如,应该xor(None, -1, [], True)返回None[]还是False?我敢打赌,对于某些人来说,每个答案都是最直观的答案。

对于True或False结果,有多达五个可能的选择:返回第一个操作数(如果它与值中的最终结果匹配,否则为布尔值),返回第一个匹配项(如果至少存在一个,否则为布尔值),返回最后一个操作数(如果... else ...),返回最后一个匹配项(如果... else ...),或始终返回布尔值。总共有5 ** 2 = 25种口味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()

5

包括我自己在内的许多人都需要一个xor功能类似于n输入异或电路的函数,其中n是可变的。(请参阅https://en.wikipedia.org/wiki/XOR_gate)。下面的简单函数实现了这一点。

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)

I / O示例如下:

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

5

要在Python中获取两个或多个变量的逻辑异或:

  1. 将输入转换为布尔值
  2. 使用按位异^或运算符(或operator.xor

例如,

bool(a) ^ bool(b)

当您将输入转换为布尔值时,按位异或将变得逻辑异或。

请注意,可接受的答案是错误的: !=由于运算符链接的微妙之处,它与Python中的xor不同

例如,使用时,以下三个值的异或是错误的!=

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

(PS我尝试编辑接受的答案以包含此警告,但我的更改被拒绝。)


4

当您知道XOR的作用时,这很容易:

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

4

这将对两个(或多个)变量进行逻辑异或

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

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

这种设置的第一个问题是,它很可能遍历整个列表两次,并且至少会两次检查至少一个元素。因此,它可能会提高代码的理解能力,但并不能提高速度(根据您的使用情况而可能有所不同)。

此设置的第二个问题是,无论变量数量如何,它都会检查排他性。乍一看,这可能是一个功能,但是随着变量数量的增加(如果有的话),第一个问题变得更加重要。


4

Xor ^在Python中。它返回:

  • 整数的按位异或
  • 布尔逻辑异或
  • 集的独家联盟
  • 实现的类的用户定义结果__xor__
  • 未定义类型的TypeError,例如字符串或字典。

如果您打算在字符串上使用它们,则将它们强制转换可以bool使您的操作变得明确(您也可能表示set(str1) ^ set(str2))。



3

这就是我编写任何真值表的方式。特别是对于xor,我们有:

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

只需查看答案列中的T值,然后将所有真实情况与逻辑或连接在一起即可。因此,可以在情况2或3中生成此真值表。因此,

xor = lambda a, b: (a and not b) or (not a and b)

-6

我们可以通过使用以下命令轻松找到两个变量的异或:

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

例:

xor(真,假)>>>真


1
xor("hey", "there")>>>正确,但这不是我们想要的
Mayou36 '18
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.