Python XOR preference: bitwise operator vs. boolean operators
在python中,是否有一个执行逻辑异或的首选方法?
例如,如果我有两个变量a和b,并且我想检查至少有一个存在,但不是两个都存在,那么我有两个方法:
方法1(按位运算符):
1 2
| if bool(a) ^ bool(b):
do x |
方法2(布尔运算符):
1 2
| if (not a and b) or (a and not b):
do x |
使用这两种方法都有内在的性能优势吗?方法2看起来更像"Python",但方法1在我看来更干净。这个相关的线程似乎表明它可能首先取决于什么变量类型的a和b!
有什么有力的论据吗?
- 另一条线以什么方式不回答你的问题?如链接线程中详细说明的,这两个值并不相等。
- "基本"?你的意思是"布尔",对吧?
- @temporalwolf:python没有"xor"布尔运算符,我需要在编写的脚本中模拟这种行为。我特别询问两种不同的XOR实现的"pythonic"风格/性能。我知道它们不是等价的。
- @板球007:是的,编辑。
- "我想检查是否至少存在一个变量,但不能同时存在两个变量"-那么这两个选项都不存在,如果您试图检查变量是否存在,那么您可能选择了一种糟糕的方法来构造程序的这一部分。
- @头晕,我会说,def xor(a, b): return (a and not b) or (b and not a)是最有可能的方法,然后在某些事情上称为xor(a, b),尽管这假设a和b是布尔值。如果需要的话,把它们包起来
- @临时狼:我喜欢!
- @头晕,值得一提的是,因为and具有更强的绑定能力,所以def xor(a, b): return a and not b or b and not a也可以工作,尽管可读性较差。
- 我不认为像用户自己提到的方法那样,将其标记为副本是个好主意。他更感兴趣的是应该优先选择哪一个以及为什么。
实现这一目标的另一种方法是使用any()和all(),如:
1 2
| if any([a, b]) and not all([a, b]):
print"Either a or b is having value" |
但根据性能,结果如下:
使用any()和all():每个回路0.542 usec
1 2
| moin@moin-pc:~$ python -m"timeit""a='a';b='b';""any([a, b]) and not all([a, b])"
1000000 loops, best of 3: 0.542 usec per loop |
使用bool(a) ^ bool(b):每个回路0.594 usec
1 2
| moin@moin-pc:~$ python -m"timeit""a='a';b='b';""bool(a) ^ bool(b)"
1000000 loops, best of 3: 0.594 usec per loop |
使用(not a and b) or (a and not b):每个回路0.0988 usec
1 2
| moin@moin-pc:~$ python -m"timeit""a='a';b='b';""(not a and b) or (a and not b)"
10000000 loops, best of 3: 0.0988 usec per loop |
显然,你的(not a and b) or (a and not b)更有效。效率大约是其他的6倍。
比较更多的and和or口味:
使用a and not b or b and not a(如temporalwolf所指):每个循环0.116 usec
1 2
| moin@moin-pc:~$ python -m"timeit""a='a';b='b';""a and not b or b and not a"
10000000 loops, best of 3: 0.116 usec per loop |
使用(a or b) and not (a and b):每个回路0.0951 usec
1 2
| moin@moin-pc:~$ python -m"timeit""a='a';b='b';""(a or b) and not (a and b)"
10000000 loops, best of 3: 0.0951 usec per loop |
注:本次业绩以a和b作为str的价值进行评价,取决于viraptor在评论中提到的__nonzero__和__bool__和__or__功能的实施情况。
- a and not b or b and not a相当。and比or具有更强的结合力。
- 这取决于__nonzero__/__bool__/__or__的实施情况。对于str来说,这是微不足道的。对于一些远程调用的对象,它不是。OP没有提到所用的值。
- @匿名的,我想你也可以看到and/or短路的好处:把它当作一个列表来理解只会把时间减半:for str in ("(bool(a) ^ bool(b))","any([a, b]) and not all([a, b])","a and not b or b and not a"): print timeit.timeit("[%s for a in range(2) for b in range(2)]" % str)。
- @维拉普:同意你的看法。这完全取决于这些函数的实现。在回答中添加了您的评论
- @temporalwolf:为您在回答中提到的表达式添加了timeit统计信息。
- @匿名的,我更了解:你的时间它在做(等价的)a = True, b = True,这意味着当你运行and/or的例子时,第二部分永远不会运行,因为它短路:我发布的列表理解迫使它尝试所有四种逻辑组合,给出短路的好处的更好的近似值(而不是让它每次都这样)。如果您执行not a and b or not b and a,它将更快,因为这利用了True/True输入的短路。
您可以使它比将问题简化为XOR更具可读性。根据上下文,这些可能更好:
1 2
| if sum((bool(a), bool(b))) == 1: # this naturally extends to more values
if bool(a) != bool(b): |
所以我认为最好的方法是使用与XOR背后的实际含义相匹配的内容。是否希望它们不具有相同的值?只有一套?还有别的吗?
如果你使用^,我在读代码,我假设你实际上想使用位运算符,这是有原因的。
Is there an inherent performance benefit to using either one?
这是一个声明。除非你知道这是性能问题,否则没关系。如果它在一个热循环中,并且你的分析器显示你确实需要优化它,那么你最好使用赛通或者其他加速它的方法。
- 在速度方面,从技术上讲,if (not a) is not (not b):会稍快一些,至少在cpython上相当于if bool(a) != bool(b):,因为not是语法(添加了UNARY_NOT指令),而bool添加了LOAD_GLOBAL和CALL_FUNCTION(两者都更昂贵)。切换到is not意味着您遵循的代码路径只处理身份平等,没有丰富的比较机制。
- 当然,但再次重申:"除非你知道这是一个性能问题,否则没关系。"我马上就知道bool(a)!=bool(b)所做的。我得花时间搞清楚(not a) is not (not b)是干什么的。