为什么"不"比Python中的"bool()"更快(或者Python函数与语句的速度)?

Set*_*ton 9 python

前几天我做了一个有趣的观察.我用不同的方式来获得一个对象,每个的速度"感实性"试验,我发现not多少速度比bool.

>>> bool([5, 6, 7])
True
>>> bool([])
False
>>> not not [5, 6, 7]
True
>>> not not []
False
>>> import timeit
>>> from numpy import mean
>>> mean(timeit.repeat('bool(a)', 'a = [5, 6, 7]', repeat=10))
0.19072036743164061
>>> mean(timeit.repeat('bool(a)', 'a = []', repeat=10))
0.18562331199645996
>>> mean(timeit.repeat('not not a', 'a = [5, 6, 7]', repeat=10))
0.072056698799133304
>>> mean(timeit.repeat('not not a', 'a = []', repeat=10))
0.073475956916809082
>>> mean(timeit.repeat('not a', 'a = [5, 6, 7]', repeat=10))
0.043941426277160647
>>> mean(timeit.repeat('not a', 'a = []', repeat=10))
0.044287109375000001
Run Code Online (Sandbox Code Playgroud)

我们可以看到使用该bool函数比使用该not语句慢得多,即使最终它们做同样的事情(返回对象的布尔状态).现在,我们都被告知在Python中,函数开销很大,但在这种情况下我并不期望这种差异,原因如下:

  • bool() 是一个内置函数,意味着它是用C语言编写的,我原本预计这会有一个相当低的开销
  • 在这两种情况下,Python必须在内部评估对象的"真实性"(我想他们会在内部使用相同的C例程来执行此操作)
    • 事实上,not必须返回对象的"真实性" 的逻辑对立面,所以理论上它正在做更多的工作(但也许有一个实现细节可以解决这个问题)

在我看来,因为两个函数基本上都在做同样的事情,所有额外的时间必须来自函数开销.如果是这种情况,为什么声明能够避免与函数相比如此多的开销?如果不是开销,为什么bool()这么慢not


更新:这里也是平均值的最小时间.

>>> min(timeit.repeat('bool(a)', 'a = [5, 6, 7]', repeat=10))
0.18180489540100098
>>> min(timeit.repeat('bool(a)', 'a = []', repeat=10))
0.1821761131286621
>>> min(timeit.repeat('not not a', 'a = [5, 6, 7]', repeat=10))
0.0707249641418457
>>> min(timeit.repeat('not not a', 'a = []', repeat=10))
0.07100605964660645
>>> min(timeit.repeat('not a', 'a = [5, 6, 7]', repeat=10))
0.04264092445373535
>>> min(timeit.repeat('not a', 'a = []', repeat=10))
0.04357004165649414
Run Code Online (Sandbox Code Playgroud)

fil*_*mor 5

(这不应该是一个答案,只是文档):这些是给定表达式的字节码序列:

bool(a):

1           0 LOAD_NAME                0 (bool)
            3 LOAD_NAME                1 (a)
            6 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
            9 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)

not a:

1           0 LOAD_NAME                0 (a)
            3 UNARY_NOT
            4 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)

not not a:

1           0 LOAD_NAME                0 (a)
            3 UNARY_NOT
            4 UNARY_NOT
            5 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)


Cha*_*ffy 2

所有函数调用都有很大的开销——毕竟,您正在创建一个新的堆栈帧来保存调用中的新局部变量。

如果这是一个更昂贵的操作,那么开销就会消失在噪音中。既然你看到的是这样一个微不足道的操作,它就很引人注目。

  • (1) 这里测试的类型的`bool`和`__bool__`方法是用C编写的,因此没有创建Python框架,只有一个非常便宜的C函数调用(以纳秒为单位)。(2) not 还必须调用 __bool__` 方法,因此这是一次调用与两次调用,而不是不调用与一次调用。 (4认同)