Aug*_*sta 22 python if-statement micro-optimization logical-operators python-2.7
一时兴起,我最近测试了这两种方法timeit,看看哪种评估方法更快:
import timeit
"""Test method returns True if either argument is falsey, else False."""
def and_chk((a, b)):
    if not (a and b):
        return True
    return False
def not_or_chk((a, b)):
    if not a or not b:
        return True
    return False
......并得到了这些结果:
 VALUES FOR a,b ->      0,0         0,1         1,0         1,1
        method
    and_chk(a,b)    0.95559     0.98646     0.95138     0.98788
 not_or_chk(a,b)    0.96804     1.07323     0.96015     1.05874
                                            ...seconds per 1,111,111 cycles.
效率的差异在1%到9%之间,总是有利于if not (a and b),这与我的预期相反,因为我理解if not a or not b它将按顺序评估其术语(if not a然后if not b),if一旦遇到真正的表达式就运行块(并且没有and条款).相反,该and_chk方法需要先评估两个子句,然后才能将任何结果返回给if not..包装子句.
然而,时间结果反驳了这种理解.那么,if评估的条件如何?我完全清楚这种程度的微观优化实际上,如果不是完全的话,毫无意义.我只是想了解Python是如何实现的.
为了完成,这就是我设置的方式timeit......
cyc = 1111111
bothFalse_and = iter([(0,0)] * cyc)
zeroTrue_and = iter([(1,0)] * cyc)
oneTrue_and = iter([(0,1)] * cyc)
bothTrue_and = iter([(1,1)] * cyc)
bothFalse_notor = iter([(0,0)] * cyc)
zeroTrue_notor = iter([(1,0)] * cyc)
oneTrue_notor = iter([(0,1)] * cyc)
bothTrue_notor = iter([(1,1)] * cyc)
time_bothFalse_and = timeit.Timer('and_chk(next(tups))', 'from __main__ import bothFalse_and as tups, and_chk')
time_zeroTrue_and = timeit.Timer('and_chk(next(tups))', 'from __main__ import zeroTrue_and as tups, and_chk')
time_oneTrue_and = timeit.Timer('and_chk(next(tups))', 'from __main__ import oneTrue_and as tups, and_chk')
time_bothTrue_and = timeit.Timer('and_chk(next(tups))', 'from __main__ import bothTrue_and as tups, and_chk')
time_bothFalse_notor = timeit.Timer('not_or_chk(next(tups))', 'from __main__ import bothFalse_notor as tups, not_or_chk')
time_zeroTrue_notor = timeit.Timer('not_or_chk(next(tups))', 'from __main__ import zeroTrue_notor as tups, not_or_chk')
time_oneTrue_notor = timeit.Timer('not_or_chk(next(tups))', 'from __main__ import oneTrue_notor as tups, not_or_chk')
time_bothTrue_notor = timeit.Timer('not_or_chk(next(tups))', 'from __main__ import bothTrue_notor as tups, not_or_chk')
...然后运行每个timeit.Timer(..)函数.timeit(cyc)以获取结果.
skr*_*sme 27
除了两次跳转(在最坏的情况下),该not_or_chk函数还需要两个一元运算,而该函数只有两次跳转(在最坏的情况下).and_chk
该DIS模块来救援!该dis模块允许您查看代码的Python字节码反汇编.例如:
import dis
"""Test method returns True if either argument is falsey, else False."""
def and_chk((a, b)):
    if not (a and b):
        return True
    return False
def not_or_chk((a, b)):
    if not a or not b:
        return True
    return False
print("And Check:\n")
print(dis.dis(and_chk))
print("Or Check:\n")
print(dis.dis(not_or_chk))
生成此输出:
And Check:
  5           0 LOAD_FAST                0 (.0)
              3 UNPACK_SEQUENCE          2
              6 STORE_FAST               1 (a)
              9 STORE_FAST               2 (b)
  6          12 LOAD_FAST                1 (a)    * This block is the *
             15 JUMP_IF_FALSE_OR_POP    21        * disassembly of    *
             18 LOAD_FAST                2 (b)    * the "and_chk"     *
        >>   21 POP_JUMP_IF_TRUE        28        * function          *
  7          24 LOAD_GLOBAL              0 (True)
             27 RETURN_VALUE
  8     >>   28 LOAD_GLOBAL              1 (False)
             31 RETURN_VALUE
None
Or Check:
 10           0 LOAD_FAST                0 (.0)
              3 UNPACK_SEQUENCE          2
              6 STORE_FAST               1 (a)
              9 STORE_FAST               2 (b)
 11          12 LOAD_FAST                1 (a)    * This block is the *
             15 UNARY_NOT                         * disassembly of    *
             16 POP_JUMP_IF_TRUE        26        * the "not_or_chk"  *
             19 LOAD_FAST                2 (b)    * function          *
             22 UNARY_NOT
             23 POP_JUMP_IF_FALSE       30
 12     >>   26 LOAD_GLOBAL              0 (True)
             29 RETURN_VALUE
 13     >>   30 LOAD_GLOBAL              1 (False)
             33 RETURN_VALUE
None
看一下我用星号标记的两个Python字节码块.这些块是你的两个反汇编函数.请注意,and_chk只有两次跳转,并且在决定是否进行跳转时进行函数计算.
另一方面,除了解释器是否进行跳转之外,该not_or_chk功能还要求not在最坏的情况下执行两次操作.