【问题标题】:Python XOR preference: bitwise operator vs. boolean operators [duplicate]Python XOR 首选项:按位运算符与布尔运算符 [重复]
【发布时间】:2017-02-27 00:59:09
【问题描述】:

是否有在 python 中进行逻辑异或的首选方法?

例如,如果我有两个变量 a 和 b,并且我想检查至少一个存在但不同时存在,我有两种方法:

方法一(按位运算符):

if bool(a) ^ bool(b):
    do x

方法2(布尔运算符):

if (not a and b) or (a and not b):
    do x

使用其中任何一种都有固有的性能优势吗?方法 2 似乎更 "pythonic" 但方法 1 对我来说看起来更干净。这个related thread 似乎表明它可能首先取决于ab 的变量类型!

有任何强有力的论据吗?

【问题讨论】:

  • 其他线程以什么方式不回答您的问题?这两个不等价,如链接线程中所述。
  • “基本”?你的意思是“布尔”,对吧?
  • @TemporalWolf:Python 没有“xor”布尔运算符,我需要在我正在编写的脚本中模拟这种行为。我专门询问两个不同 xor 实现的“pythonic”风格/性能。我知道它们是不等价的。
  • @dizzyf 我会说def xor(a, b): return (a and not b) or (b and not a) 是最pythonic 的方式,然后调用xor(a, b),尽管假设ab 是布尔值。如果需要,将它们包装起来
  • 我认为将其标记为重复不是一个好主意,因为用户自己提到了这些方法。他更感兴趣的是应该首选哪个以及为什么

标签: python xor


【解决方案1】:

实现它的另一种方法是使用any()all(),例如:

if any([a, b]) and not all([a, b]):
    print "Either a or b is having value"

但根据性能,以下是结果:

  1. 使用 any()all()每个循环 0.542 微秒

    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
    
  2. 使用bool(a) ^ bool(b)每个循环 0.594 微秒

    moin@moin-pc:~$ python -m "timeit" "a='a';b='b';" "bool(a) ^ bool(b)"
    1000000 loops, best of 3: 0.594 usec per loop
    
  3. 使用(not a and b) or (a and not b)每个循环 0.0988 微秒

    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 倍。


andor 的其他几种风格的比较:

  1. 使用 a and not b or b and not a(由 TemporalWolf 指出):每个循环 0.116 微秒

    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
    
  2. 使用 (a or b) and not (a and b):每个循环 0.0951 微秒

    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
    

注意: 此性能针对ab 的值评估为str,并取决于__nonzero__ / __bool__ / __or__ 的实现functions 正如 viraptor 在评论中提到的那样。

【讨论】:

  • a and not b or b and not a 是等价的。 andor 有一个 stronger binding
  • 这取决于__nonzero__/__bool__/__or__的实现。对于 str 来说,这是微不足道的。对于远程调用的东西,它不是。 Op 对使用的值只字未提。
  • @anonymous 我认为您可能还会看到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)
  • @viraptor:同意你的观点。这完全取决于这些功能的实现。用答案添加了您的评论
  • @TemporalWolf:为您在答案中提到的表达式添加了 timeit 统计信息
【解决方案2】:

与将问题简化为 XOR 相比,您可以使其更具可读性。根据上下文,这些可能会更好:

if sum((bool(a), bool(b))) == 1:  # this naturally extends to more values
if bool(a) != bool(b):

所以我认为最好的方法是使用与 XOR 背后的实际含义相匹配的内容。您是否希望它们不具有相同的价值?只有其中一个设置?还有什么?

如果您使用 ^ 并且我正在阅读代码,我会假设您实际上想要使用按位运算符并且出于某种原因它很重要。

使用其中任何一个都有固有的性能优势吗?

这是一个声明。除非您知道这是性能问题,否则没关系。如果它处于热循环中并且您的分析器显示您确实需要对其进行优化,那么您最好使用 Cython 或其他加速它的方法。

【讨论】:

  • 为了速度,从技术上讲,if (not a) is not (not b): 会稍微快一些,并且相当于if bool(a) != bool(b):,至少在 CPython 上是这样,因为not 是语法(添加了UNARY_NOT 指令),而bool添加了LOAD_GLOBALCALL_FUNCTION(两者都更贵)。切换到is not 意味着您遵循只处理身份相等的代码路径,没有丰富的比较机制。
  • 当然,但同样:“除非您知道这是性能问题,否则没关系。”我马上就知道bool(a)!=bool(b) 做了什么。我必须花时间弄清楚(not a) is not (not b) 做了什么。
猜你喜欢
  • 1970-01-01
  • 2011-04-20
  • 1970-01-01
  • 2015-10-22
  • 2017-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-01
相关资源
最近更新 更多