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

967

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

6
你如何定义一对字符串的“异或(xor)”操作?你认为对于“abc” ^ ""这个操作,它应该返回什么而实际上它却不返回这个值? - Mehrdad Afshari
24
由于根据Python的bool类型定义,只有其中一个字符串是True,因此应该返回True而不是引发异常。 - Zach Hirsch
71
我惊讶于Python没有一个称为“异或”的中缀运算符,这将是最直观、最符合Python风格的实现。使用“^”与其他语言保持一致,但不如Python的大部分代码那样易读。 - Mark E. Haase
20
你的问题的明显答案是,a xor a 被定义为 (a and not b) or (not a and b),因此当 ab 为字符串或任何其他类型时,a xor b 应该产生 (a and not b) or (not a and b) 的结果。 - Kaz
2
问题在于文档不够详细。"^"是按位异或,字面意思是逐位进行,而不是逐个布尔值进行。因此,x'FFFF00' ^ x'FFFF00'应该是x'000000'。或者这只是针对char的逐个比较?需要将其转换为数字?我们需要迭代较短字符串的字符以匹配较长字符串的长度。所有这些都应该内置。 - mckenzm
如果这有帮助的话,int("0xFFFF00", 16) ^ int("0xFFFF00", 16) == int(0),所以你必须将其转换为数字并在结果上调用 hex,然后在左侧填充足够多的 0 以达到最长长度。 - Guimoute
29个回答

6

在Python中,Xor 表示为符号 ^。它有以下用途:

  • 对于整数,进行按位异或计算。
  • 对于布尔型,进行逻辑异或计算。
  • 对于集合,进行独占式的并集计算。
  • 对于实现了__xor__方法的自定义类,返回其特定结果。
  • 对于未定义类型(例如字符串或字典),抛出TypeError。

如果您想要在字符串上使用 Xor 操作,建议将其转换为布尔值以避免歧义(也可以使用set(str1) ^ set(str2) 进行集合操作)。


4
这个函数可以获取两个(或多个)变量的逻辑异或。
str1 = raw_input("Enter string one:")
str2 = raw_input("Enter string two:")

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

这个设置的第一个问题是它很可能会遍历整个列表两次,并且至少会检查其中一个元素两次。所以虽然它可能增加了代码的理解,但并不利于速度(这可能因您的用例而略有不同)。
这个设置的第二个问题是它无论变量的数量如何都会检查排他性。这起初可能被认为是一种特性,但随着变量数量的增加(如果有的话),第一个问题变得更加重要。

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

3

XOR在operator.xor中实现。


8
operator.xor 对应于位运算,这就是原帖作者不想要的那种运算。 - Niriel
@kojiro 显然如此! - Arel

2
这是一个map-reduce通用化的实现。请注意,这相当于functools.reduce(lambda x, y: x != y, map(bool, orands))
def xor(*orands):
    return bool(sum(bool(x) for x in orands) % 2)

如果你正在寻找一个单热检测器,这里有一个概括。这个概括可能符合英语中异或的使用(例如“花一美元可以买果汁或咖啡或茶”),但它不符合典型的操作顺序。例如: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

您可以使用以下任一种方式进行测试:

# 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检测器输出:

000|假
001|真
010|真
011|假
100|真
101|假
110|假
111|假
总共真:3

Map-Reduce输出:

000|假
001|真
010|真
011|假
100|真
101|假
110|假
111|真
总共真:4


你处理3个或运算的情况有误:1 XOR 1 XOR 1 应该是 True,而不是 111|False。这很容易修复:只需将总和的 % 2 添加到函数中即可。然而,这使它几乎与 @Phillip M. Feldman 的答案相同。 - Alex Potapenko
@AlexPotapenko,是的,你说得对。我已经在答案中加入了我的新理解。 - mathandy
1
你现在可能有一个小的笔误:应该是 bool(sum(bool(x) for x in orands) % 2),否则它总是 True,除了所有 False 的 orands,因为所有非零和都会转换为 True。你之前的 == 1 方法也完全有效:sum(bool(x) for x in orands) % 2 == 1 - Alex Potapenko

1

仅仅因为我在其他地方没有看到提到过,这个也可以解决问题:

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

我不确定它是否比已接受的解决方案bool(a) != bool(b)更好/更易读/更符合Python风格。


0
Python处理逻辑运算的方式可能会让人感到困惑,因此我的实现提供了一个选项(默认情况下)以获得简单的True/False答案。如果将可选的第三个参数设置为None,则可以获得实际的Python结果。
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

-2
我做了一些测试,这个解决方案在效率和灵活性方面似乎是一个不错的选择。它利用了高效的内置函数,并且不执行不必要的操作 - 如果条件失败,它会提前停止。它测试真值。
我找到了一个解决方案,在提前停止方面表现更好,但对于长迭代对象来说效率非常低下。
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

-11

我们可以通过以下方式轻松地找到两个变量的异或值:

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

示例:

xor(True,False) >>> True


2
xor("hey", "there") >>> True,但这不是我们想要的。 - Mayou36

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接