在Python中,为什么“if rank:”比“if rank != 0:”更快?

5
当我改变时
for i in range(0, 100):
    rank = ranks[i]
    if rank != 0:
        pass

to:

for i in range(0, 100):
    rank = ranks[i]
    if rank:
        pass

我发现第二段代码更加高效,为什么?

进行基准测试,在我的情况下,ranks是一个整数的numpy数组。差异更大。

import numpy as np
import time
N = 1000000
ranks = np.random.random_integers(0, 10, N)
start = time.time()
for i in range(0, N):
    rank = ranks[i]
    if rank != 0:
        pass

print time.time() - start
start = time.time()
for i in range(0, N):
    rank = ranks[i]
    if rank:
        pass
print time.time() - start
start = time.time()
for i in range(0, N):
    if i != 0:
        pass

print time.time() - start
start = time.time()
for i in range(0, N):
    if i:
        pass
print time.time() - start

输出:

1.15917396545
0.45020198822
0.123136997223
0.122531175613

我期望会有一些提升,因为较少的工作发生在解释的 Python 中,更多的工作发生在内置方法中,但在一个真正的程序中,这可能不会带来太大的影响。(仍然值得这样做,因为这是标准风格,而且代码更少。) - user2357112
1个回答

8

将核心内容提炼出来的检查

for i in range(0,100):
  if i != 0:
    pass

并且

for i in range(0,100):
  if i:
    pass

我们看到有所不同。
$ python -m timeit 'for i in range(0,100):' ' if i != 0:' '  pass'
100000 loops, best of 3: 4.69 usec per loop
$ python -m timeit 'for i in range(0,100):' ' if i:' '  pass'
100000 loops, best of 3: 4.18 usec per loop

第一个情况涉及与零进行比较,而第二个情况只是测试是否为false。
要查看它的操作,请使用dis
>>> def f():
...  for i in range(0,100):
...    if i:
...      pass
...
>>> def g():
...  for i in range(0,100):
...    if i != 0:
...      pass
...
>>> from dis import dis
>>> dis(f)
  2           0 SETUP_LOOP              32 (to 35)
              3 LOAD_GLOBAL              0 (range)
              6 LOAD_CONST               1 (0)
              9 LOAD_CONST               2 (100)
             12 CALL_FUNCTION            2
             15 GET_ITER
        >>   16 FOR_ITER                15 (to 34)
             19 STORE_FAST               0 (i)

  3          22 LOAD_FAST                0 (i)
             25 POP_JUMP_IF_FALSE       16

  4          28 JUMP_ABSOLUTE           16
             31 JUMP_ABSOLUTE           16
        >>   34 POP_BLOCK
        >>   35 LOAD_CONST               0 (None)
             38 RETURN_VALUE
>>> dis(g)
  2           0 SETUP_LOOP              38 (to 41)
              3 LOAD_GLOBAL              0 (range)
              6 LOAD_CONST               1 (0)
              9 LOAD_CONST               2 (100)
             12 CALL_FUNCTION            2
             15 GET_ITER
        >>   16 FOR_ITER                21 (to 40)
             19 STORE_FAST               0 (i)

  3          22 LOAD_FAST                0 (i)
             25 LOAD_CONST               1 (0)   <-- this only happens in != 0
             28 COMPARE_OP               3 (!=)  <-- this only happens in != 0
             31 POP_JUMP_IF_FALSE       16

  4          34 JUMP_ABSOLUTE           16
             37 JUMP_ABSOLUTE           16
        >>   40 POP_BLOCK
        >>   41 LOAD_CONST               0 (None)
             44 RETURN_VALUE

在我的情况下,ranks是一个numpy的整数数组。看起来差异很大。 - BerSerK
如果您dis两个样本,则它们的差别仍为LOAD_CONST 1 (0)COMPARE_OP 3 (!=) - SheetJS
@sza 简单来说,True 是一个全局变量,因此 Python 执行的操作是 LOAD_GLOBAL 而不是 LOAD_CONST - SheetJS
True 是一个内置变量。Python 必须执行两个字典查找(一个在全局字典中,一个在内置字典中)才能弄清楚 True 是什么。在 Python 3 中,情况已经不再如此。 - user2357112
1
在 Py2k 中,您可以执行 True,False = False,True,这使得 False == not True,但是 False 现在是 True,而 True 现在是 False - Snakes and Coffee
显示剩余4条评论

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