【问题标题】:if a == b or a == c: vs if a in {b, c}:if a == b or a == c: vs if a in {b, c}:
【发布时间】:2018-01-29 17:21:03
【问题描述】:

在我的代码中,我曾经相当频繁地进行if a == b or a == c or a == d: 之类的比较。在某些时候,我发现如果这些值不可散列,这些可以很容易地缩短为if a in {b, c, d}:if a in (b, c, d):。但是,我从未在其他任何人的代码中看到过这样的构造。这可能是因为:

  1. == 方式比较慢。
  2. == 方式更符合 Python 风格。
  3. 他们实际上做了微妙的不同的事情。
  4. 我碰巧没有看过任何需要的代码。
  5. 我看过它,只是忽略或忘记了它。
  6. 不需要进行这样的比较,因为其他地方的代码会更好。
  7. 除了我,没有人想到过in

如果有的话,是什么原因?

【问题讨论】:

  • 你还没有看到if a in (b, c, d):?如果没有,你还没有看够。这是非常常见的,所以 5 或 .. 以及 8.
  • 第 3 点是正确的,但这通常不是为什么一个比另一个更受欢迎的原因。
  • 1 可能是真的,尤其是对于较大的值集,但我认为你的意思是相反的。
  • 从这个可能的重复、它的答案和它的链接问题中可以看出,这确实是一个非常常见的结构,并且可以满足您的期望。
  • @Ev.Kounis 同样,bc 保证在a in (b,c) 的情况下被评估,但a==b or a==c 可能会短路。

标签: python


【解决方案1】:

对于简单值(即不是表达式或NaNs),if a == b or a == cif a in <iterable of b and c> 是等价的。

如果值是可散列的,最好使用带有 set literalin 而不是元组或列表文字:

if a in {b, c}: ...

CPython 的 peephole optimiser 通常能够将其替换为缓存的 frozenset() 对象,并且针对集合的成员资格测试是 O(1) 操作。

【讨论】:

  • Nitpick:它们并不完全等同,尽管在实践中发生的许多情况下它们很可能是可以互换的。考虑a = b = c = float('nan')的情况:a == b or a == cFalse,而a in {b, c}True
  • @MarkDickinson 为什么是第一个False
  • @Ev.Kounis 因为标准 IEE754,NaN 是唯一不等于自身的元素,这正是你应该如何测试它!
  • @Ev.Kounis a = b = c = float('nan') ; print(a == b) ; False
  • @Ev.Kounis 我不这么认为,NaN 表示不是数字,它与数据库中的 NULL 是相同的问题。你怎么能比较你不知道的东西。因此它不比较,但有一个真值很方便,所以它比较 False 而不是引发错误
【解决方案2】:

性能方面:“in”更好

timeit.timeit("pub='1'; pub == 1 or pub == '1'")
0.07568907737731934
timeit.timeit("pub='1'; pub in[1, '1']")
0.04272890090942383
timeit.timeit("pub=1; pub == 1 or pub == '1'")
0.07502007484436035
timeit.timeit("pub=1; pub in[1, '1']")
0.07035684585571289

“in”还确保代码不重复 a == 1 或 a == 2 是重复的。 而且不好读。 “in”只是让它更容易理解。这是简单而优雅的代码实践的案例之一。简而言之,如果我们还没有使用“in”,我们(应该)更频繁地使用它。

【讨论】:

  • 尝试向timeit() 函数添加一个大的number 参数并注意结果开始发生变化。
【解决方案3】:

我很想知道直接比较与检查数组之间的时间差异是什么。

结论:构建数组的成本不是免费的,在考虑速度差异时必须考虑到。

如果在比较时正在构建数组,则从技术上讲它比简单比较慢。因此,简单的比较在循环内或循环外会更快。

也就是说,如果数组已经构建好了,那么在大循环中检查数组会比进行简单的比较更快。

$ speed.py
inarray                   x 1000000:  0.277590343844
comparison                x 1000000:  0.347808290754
makearray                 x 1000000:  0.408771123295
import timeit

NUM = 1000000

a = 1
b = 2
c = 3
d = 1

array = {b,c,d}
tup = (b,c,d)
lst = [b,c,d]

def comparison():
    if a == b or a == c or a == d:
        pass

def makearray():
    if a in {b, c, d}:
        pass

def inarray():
    if a in array:
        pass

def maketuple():
    if a in (b,c,d):
        pass

def intuple():
    if a in tup:
        pass

def makelist():
    if a in [b,c,d]:
        pass

def inlist():
    if a in lst:
        pass


def time_all(funcs, params=None):
    timers = []
    for func in funcs:
        if params:
            tx = timeit.Timer(lambda: func(*params))
        else:
            tx = timeit.Timer(lambda: func())
        timers.append([func, tx.timeit(NUM)])

    for func, speed in sorted(timers, key=lambda x: x[1]):
        print "{fn:<25} x {n}: ".format(fn=func.func_name, n=NUM), speed
    print ""
    return

time_all([comparison,
          makearray,
          inarray,
          intuple,
          maketuple,
          inlist,
          makelist
          ], 
         )

这并不能完全回答您关于您不经常看到使用 in 进行比较的原因的问题。我会推测,但它可能是 1、2、4 的混合,以及作者需要的情况编写特定的代码。

我个人根据情况使用了这两种方法。选择通常归结为速度或简单性。


编辑:

@bracco23 是对的,使用元组 vs 数组 vs 列表会改变时间。

$ speed.py
inarray                   x 1000000:  0.260784980761
intuple                   x 1000000:  0.288696420718
inlist                    x 1000000:  0.311479982167
maketuple                 x 1000000:  0.356532747578
comparison                x 1000000:  0.360010093964
makearray                 x 1000000:  0.41094386108
makelist                  x 1000000:  0.433603059099

【讨论】:

  • 我添加了一些使用元组而不是数组的测试。当使用已经存在的元组时,它的性能比比较好,但比已经存在的数组差。当我们动态创建元组时,它的性能比创建数组更好,并且或多或少类似于比较(它们在有序列表中交换了几次位置)
  • 好电话。我已经编辑了回复以反映这些差异。元组、列表、数组的时间都略有不同。
  • 出于好奇,我修改了您的测试以使用使用list(range(1,1000)) + [0]a = 0 创建的列表(以便在冗长列表的末尾找到a);当然,我只为(预制的)列表、元组、集合和frozenset 运行它,跳过了or-ed 比较。结果完全不同:frozenset 0.21,set 0.24,tuple 6.72,list 7.15。显然 Python 是在元组或列表中进行线性搜索,而不是将其隐式转换为集合。对于 Python3,差异更大(~0.19 vs ~9.8),
  • @Błotosmętek 我看到的数字略有不同。设置 0.26,frozenset 0.27,元组 14.49,列表 14.72。 CPU可以解释时序差异,但我不太明白的是我们之间的set与frozenset顺序的差异。我认为我们应该看到相同的时间比率。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-16
  • 2017-01-11
  • 1970-01-01
  • 1970-01-01
  • 2015-06-15
  • 1970-01-01
相关资源
最近更新 更多