python - 为什么 timeit() 函数在传递函数和字符串表达式时返回不同的结果?
问题描述
我正在我的 Python REPL 中尝试 pythontimeit
函数。它可以通过两种方式对小段代码进行计时:作为可调用的或作为带引号的表达式。我想知道为什么下面的代码会产生不同的计时结果。
>>> import timeit
>>> timeit.timeit("lambda *args: None")
0.058281898498535156
>>> timeit.timeit(lambda *args: None)
0.0947730541229248
>>>
我的直觉告诉我应该有更多与引用的字符串变体相关的“开销”,因为它需要解释,但情况似乎并非如此。但显然我的直觉是错误的..
这是另一个代码片段。调用可调用函数与计时引用的函数语句之间似乎没有很大的时间差异:
>>> def costly_func():
... return list(map(lambda x: x^2, range(10)))
...
>>> import timeit
>>> timeit.timeit(costly_func)
2.421797037124634
>>> timeit.timeit("list(map(lambda x: x^2, range(10)))")
2.3588619232177734
解决方案
观察:
>>> def costly():
... return list(map(str, list(range(1_000_000))))
...
>>> timeit.timeit(costly, number=100)
30.65105245400082
>>> timeit.timeit('costly', number=1_000_000_000, globals=globals())
27.45540758000061
看number
论据。执行 costly
100 次函数需要 30 秒。执行表达式 costly
1'000'000'000 (!) 次花费了将近 30 秒。
为什么?因为第二个代码没有执行函数costly
!它唯一执行的是表达式costly
:注意缺少括号,这意味着它不是函数调用。该表达式costly
基本上是一个无操作(好吧,它只需要检查名称“costly”是否存在于当前范围内,仅此而已),这就是它如此之快的原因,如果 Python 足够聪明,可以优化它,执行表达式costly
(不是 costly()
!)将是瞬时的!
在您的情况下,说lambda *args: None
只是定义一个匿名函数,对吗?当您执行此确切代码时,会创建一个新函数,但不会执行(为此,您应该调用它:)(lambda *args: None)()
。
因此,对字符串 "lambda *args: None"
进行计时timeit.timeit("lambda *args: None")
基本上可以测试 Python 吐出新匿名函数的速度。
通过测试 Python执行现有函数的速度来为函数本身计时。timeit.timeit(lambda *args: None)
吐出新创建的函数是小菜一碟,而实际运行它们可能非常困难。
以这段代码为例:
def Ackermann(m, n):
if m == 0:
return n + 1
if m > 0:
if n == 0:
return Ackermann(m - 1, 1)
elif n > 0:
return Ackermann(m - 1, Ackermann(m, n - 1))
如果你把那个确切的代码放在一个字符串timeit
中,你会得到这样的:
>>> code = """def Ackermann(m, n):
... if m == 0:
... return 0
... if m > 0:
... if n == 0:
... return Ackermann(m - 1, 1)
... elif n > 0:
... return Ackermann(m - 1, Ackermann(m, n - 1))"""
>>> timeit.timeit(code, number=1_000_000)
0.10481472999890684
现在尝试timeit
函数本身:
>>> timeit.timeit(lambda : Ackermann(6, 4), number=1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/timeit.py", line 232, in timeit
return Timer(stmt, setup, timer, globals).timeit(number)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/timeit.py", line 176, in timeit
timing = self.inner(it, self.timer)
File "<timeit-src>", line 6, in inner
File "<stdin>", line 1, in <lambda>
File "<stdin>", line 8, in Ackermann
File "<stdin>", line 8, in Ackermann
File "<stdin>", line 8, in Ackermann
[Previous line repeated 1 more time]
File "<stdin>", line 6, in Ackermann
File "<stdin>", line 8, in Ackermann
File "<stdin>", line 6, in Ackermann
File "<stdin>", line 8, in Ackermann
File "<stdin>", line 8, in Ackermann
File "<stdin>", line 8, in Ackermann
[Previous line repeated 983 more times]
File "<stdin>", line 6, in Ackermann
File "<stdin>", line 2, in Ackermann
RecursionError: maximum recursion depth exceeded in comparison
看 - 你甚至不能运行它!实际上,可能没有人可以,因为它是如此的递归!
但是,为什么第一次调用成功了?因为它没有执行任何东西,它只是吐出了很多新功能,并在不久之后将它们全部删除。
推荐阅读
- php - 获取未定义的变量:index.php 中的 pdo
- python - 根据条件向 DataFrame 添加新行
- macos - Mac OSX 将 smb 从脚本挂载到 /Volumes
- c++ - 在二维数组c ++ 98中循环移动第二行
- apache-kafka - kafka 是否支持开箱即用的模式注册表,或者它是一个融合的平台功能?
- reactjs - React-Native:何时使用类或功能组件?
- php - 如何重新格式化 PHP 代码,返回重复的属性类?
- javascript - 在传递之前按下按钮时验证值
- javascript - 未捕获的类型错误:无法读取 node.js 中未定义的属性“通道”
- spring-boot - 使用 sprint-boot 的 web-socket 反向代理因错误 text_partial_writing 断开连接