timeit ValueError:stmt既不是字符串也不是可调用的

lia*_* li 5 python callable timeit

timeit在Python中玩过,遇到了一个奇怪的问题。

我定义一个简单的函数addtimeit当我传递add两个字符串参数时,可以工作。但是ValueError: stmt is neither a string nor callable当我传递add两个int参数时,它会增加。

>>> import timeit
>>> def add(x,y):
...     return x + y
... 


>>> a = '1'
>>> b = '2'
>>> timeit.timeit(add(a,b))
0.01355926995165646


>>> a = 1
>>> b = 2
>>> timeit.timeit(add(a,b))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/anaconda/lib/python3.6/timeit.py", line 233, in timeit
    return Timer(stmt, setup, timer, globals).timeit(number)
  File "/anaconda/lib/python3.6/timeit.py", line 130, in __init__
    raise ValueError("stmt is neither a string nor callable")
ValueError: stmt is neither a string nor callable
Run Code Online (Sandbox Code Playgroud)

为什么这里的参数类型很重要?

Voi*_*ler 12

使用字符串版本 add 返回一个字符串,timeit 可以计算该字符串。所以 "12" 是一个有效的 python 表达式,而 3 不是。

timeit.timeit("12") # works
timeit.timeit(3) # does not
Run Code Online (Sandbox Code Playgroud)

使用 timeit 的最佳方法是使用 lambda 包装要测试的函数:

timeit.timeit(lambda: add(1,2))
Run Code Online (Sandbox Code Playgroud)

这比弄乱字符串要优雅得多。我真的不明白为什么所有的例子都使用字符串。


Mar*_*ers 7

您的错误是假设Python将表达式传递add(a, b)timeit()。并非如此,add(a, b)不是字符串,而是表达式,因此Python会执行 add(a, b),并将调用结果传递给timeit()调用。

因此,add('1', '2')结果是'12',一个字符串。将字符串传递给timeit()是可以的。但是add(1, 2)12,一个整数。timeit(12)给你一个例外。'12'当然,计时并不是那么有趣,但这是一个有效的Python表达式,产生整数值12:

>>> import timeit
>>> def add(x,y):
...     return x + y
...
>>> a = '1'
>>> b = '2'
>>> add(a, b)
'12'
>>> timeit.timeit('12')
0.009553937998134643
>>> a = 1
>>> b = 2
>>> add(a, b)
12
>>> timeit.timeit(12)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/.../lib/python3.7/timeit.py", line 232, in timeit
    return Timer(stmt, setup, timer, globals).timeit(number)
  File "/.../lib/python3.7/timeit.py", line 128, in __init__
    raise ValueError("stmt is neither a string nor callable")
ValueError: stmt is neither a string nor callable
Run Code Online (Sandbox Code Playgroud)

这完全是很正常的。否则,如何将一个函数的结果直接传递给另一个函数?timeit.timeit()只是一个Python函数,没有什么特别之处,它会禁用表达式的一般评价。

您想要的是将带有表达式字符串传递给timeit()timeit()不能访问您的add()函数或ab,因此您需要使用第二个参数(设置字符串)为它提供访问权限。您可以from __main__ import add, a, b用来导入ad函数对象:

timeit.timeit('add(a,b)', 'from __main__ import add, a, b')
Run Code Online (Sandbox Code Playgroud)

现在您可以获得更有意义的结果:

>>> import timeit
>>> def add(x,y):
...     return x + y
...
>>> a = '1'
>>> b = '2'
>>> timeit.timeit('add(a,b)', 'from __main__ import add, a, b')
0.16069997000158764
>>> a = 1
>>> b = 2
>>> timeit.timeit('add(a,b)', 'from __main__ import add, a, b')
0.10841095799696632
Run Code Online (Sandbox Code Playgroud)

因此,添加整数比添加字符串快。您可能想尝试使用不同大小的整数和字符串,但是添加整数将保持较快的结果。


wim*_*wim 5

我的问题是为什么参数类型在这里很重要?

在调用函数之前,将对函数参数进行全面评估。这意味着您执行以下操作:

timeit.timeit(add(a,b))
Run Code Online (Sandbox Code Playgroud)

然后add(a,b)就已经被timeit使用过了。因此,它没有时间。

timeit.timeit(add(a,b))当a和b是数字字符串时,“有效” 的原因只是一个愚蠢的选择:它正在计时'12'。调用的结果add('1', '2')恰好是一个有效的Python代码字符串。 timeit对其进行编译,并假定您想对文字整数12的求值时间进行计时。