为什么"not"运算符比 python 中的常规条件更快?



我读了一篇关于列表理解的文章,其中提到列表理解比常规的更快,并且嵌套循环。在阅读那篇文章时,它提到了

if expression % 2 == 0:

比慢

if not expression % 2:

有人能解释这背后的原因吗?即使我在解决树的问题,在递归中,我也使用而不是root类型的参数。如果有人能帮忙,我将不胜感激。

文章链接:https://switowski.com/blog/for-loop-vs-list-comprehension这是一个简短的实现,感谢@Ted

>>> MILLION_NUMBERS = list(range(1_000_000))
>>> def for_loop():
...     output = []
...     for element in MILLION_NUMBERS:
...         if not element % 2:
...             output.append(element)
...     return output
... 
>>> def for_loop2():
...     output = []
...     for element in MILLION_NUMBERS:
...         if element % 2 == 0:
...             output.append(element)
...     return output
... 
>>> timeit.timeit(stmt=for_loop, number=100)
6.254316797000001
>>> timeit.timeit(stmt=for_loop2, number=100)
7.362754617999997

提前谢谢。

如果在这两种情况下都使用反汇编程序,您会发现:

如果表达式%2==0:


2           0 LOAD_FAST                0 (x)
2 LOAD_CONST               1 (2)
4 BINARY_MODULO
6 LOAD_CONST               2 (0)
8 COMPARE_OP               2 (==)
10 POP_JUMP_IF_FALSE       20

如果不是表达式%2:

2           0 LOAD_FAST                0 (x)
2 LOAD_CONST               1 (2)
4 BINARY_MODULO
6 POP_JUMP_IF_TRUE        16

上面的例子必须再做两次操作来确定该值是否为零。这可能是该书作者所说的依据。事实上,这几乎没有什么区别,而且在python的实现中可能会有很大的不同。您也可以使用timeit模块来计时!

>>> timeit.timeit('5 % 2 == 0', number=10**7)
0.47088638799323235
>>> timeit.timeit('not 5 % 2', number=10**7)
0.1560280679987045

这会告诉你第二个比第一个快3倍。

主要原因是Python不知道expression是整数;它可能是一个覆盖运算符的对象,因此expression % 2 == 0可能意味着调用expression.__mod__(2).__eq__(0).__bool__()(或.__len__()(。

我不确定Python是否真的调用了它们或将它们优化为整数,但无论哪种方式,它都必须在每次循环中查找它们。每个方法查找都需要一点时间。

同时,not expression % 2最多可以是expression.__mod__(2).__bool__()(或.__len__()(——跳过对==的查找(没有对not的覆盖(。

最新更新