为什么在传递函数与字符串表达式时,timeit()函数返回不同的结果?

时间:2019-03-15 16:44:05

标签: python timeit

我正在尝试在我的Python REPL中使用python timeit函数。它可以通过两种方式对小代码段进行计时:作为可调用的或带引号的表达式。我想知道为什么以下代码会产生不同的计时结果。

>>> 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

1 个答案:

答案 0 :(得分:1)

观察:

>>> 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花费了30秒。 执行表达式 costly 1'000'000'000(!)次几乎花了30秒。

为什么?因为第二个代码执行函数costly!它唯一执行的是表达式costly:注意缺少括号,这意味着它不是函数调用。表达式costly基本上是无操作的(嗯,它只需要检查当前范围内是否存在“代价昂贵”的名称,仅此而已),这就是为什么它是如此之快以及Python是否足够聪明来优化这样,表达式costly不是 costly()!)的执行将是瞬间的!

在您的情况下,说lambda *args: None只是定义一个匿名函数,对吗?当您执行这个确切的代码时,会创建一个新函数,但不会执行(为此,您应该调用它:(lambda *args: None)())。< / p>

因此,用"lambda *args: None" string timeit.timeit("lambda *args: None")进行计时基本上可以测试Python吐出新的匿名函数的速度。

使用timeit.timeit(lambda *args: None)函数本身进行计时,以测试Python可以执行一个现有的 函数的速度。

吐出新创建的功能可谓小菜一碟,而实际上运行它们可能真的很困难。

以下面的代码为例:

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

看-你甚至不能运行它!实际上,可能没有人可以这样做,因为它是如此的递归!

为什么第一次呼叫成功?因为它什么也没执行,所以它吐出了许多新功能,并在不久后将其全部删除。