使用 timeit.Timer() 时如何传递函数的参数
Posted
技术标签:
【中文标题】使用 timeit.Timer() 时如何传递函数的参数【英文标题】:how to pass parameters of a function when using timeit.Timer() 【发布时间】:2011-07-02 11:13:21 【问题描述】:这是一个简单程序的大纲
# some pre-defined constants
A = 1
B = 2
# function that does something critical
def foo(num1, num2):
# do something
# main program.... do something to A and B
for i in range(20):
# do something to A and B
# and update A and B during each iteration
import timeit
t = timeit.Timer(stmt="foo(num1,num2)")
print t.timeit(5)
我只是不断收到“未定义全局名称 foo”..... 谁可以帮我这个事?谢谢!
【问题讨论】:
首先,这里的缩进令人困惑。看起来foo
的定义范围与t
不同...
嗨。我修复了缩进。现在看起来好多了吗? :] 谢谢。
timeit 对于单行来说是可以的,但对于其他更好的方法,请检查此方法***.com/questions/5478351/…
在python3中有一个非常简单的解决方案,你可以通过在timeit调用中添加globals=globals()
参数在全局命名空间中运行表达式:***.com/a/51913199/2925963
【参考方案1】:
有一个更简单的解决方案(至少对于 Python 3),您可以使代码在您当前的全局命名空间内执行:
t = timeit.Timer(stmt="foo(num1,num2)", globals=globals())
https://docs.python.org/3/library/timeit.html#examples 我知道全局变量不是首选,但如果您只是制作一个快速脚本来检查某些内容,我认为这是最简单的实现。
【讨论】:
小心重复,因为结果将被缓存在全局变量中。【参考方案2】:函数可以使用timeit
中的参数,如果这些是使用闭包创建的,我们可以通过将它们包装在另一个函数中来添加此行为。
def foo(num1, num2):
def _foo():
# do something to num1 and num2
pass
return _foo
A = 1
B = 2
import timeit
t = timeit.Timer(foo(A,B))
print(t.timeit(5))
或更短,我们可以使用functools.partial 代替显式闭包声明
def foo(num1, num2):
# do something to num1 and num2
pass
A = 1
B = 2
import timeit, functools
t = timeit.Timer(functools.partial(foo, A, B))
print(t.timeit(5))
使用 lambda 进行编辑,感谢 @jupiterbjy
我们可以使用不带参数的 lambda 函数来代替 functools 库
def foo(num1, num2):
# do something to num1 and num2
pass
A = 1
B = 2
import timeit
t = timeit.Timer(lambda: foo(A, B))
print (t.timeit(5))
【讨论】:
谢谢,functools 帮助我避免了全局变量、讨厌的字符串方法以及装饰器/闭包业务。 @Peter.k 那是因为如果你只想使用timeit.timeit()
它需要以字符串的形式输入你的函数,例如 timeit.timeit('foo')
谢谢,这正是我想要的!还应该注意,您甚至不需要实例化Timer
;这也有效:timeit.timeit(functools.partial(foo, A, B), number=5)
对于当前的 CPython3.8,与 functools.partial(foo, A, B)
相比 timeit
调用 lambda: foo(A, B)
会是个坏主意吗?
@jupiterbjy 你是对的,你可以使用函数 lambda 代替 functools 库【参考方案3】:
您必须在设置字符串中创建变量。在这里,我导入函数,并创建我传递给它的变量之一。我还通过将其转换为 stmt 字符串来设置其中一个变量
SETUP = '''
from __main__ import policy_iteration
from environments.gridworld import GridworldEnv
env = GridworldEnv()
'''
discount = 5
timeit.timeit("policy_iteration(env,discount_factor="+str(discount)+")",
setup= SETUP,
number=10))
【讨论】:
【参考方案4】:另一种选择是通过functools 将函数绑定到它的参数(类似于std::bind)。然后你不需要将参数传递给 timeit,functool.partial
返回的 callable 会处理这个问题:
def findMax(n):#n is an array
m = 0
c = 0
for i in range(len(n)):
c += 1
if m < n[i]:
m = n[i]
return m, c
import timeit
import functools
a = [6, 2, 9, 3, 7, 4, 5]
t = timeit.Timer(functools.partial(findMax,a))
t.timeit(100)
【讨论】:
对我来说这是迄今为止最简单的解决方案。它也适用于类函数 =) 有什么缺点/副作用吗?【参考方案5】:我今天在 Python 3.7 中玩弄计时,并尝试将函数和变量传递给计时器。这就是我想出的。
import re
text = "This is a test of the emergency broadcast system"
def regex(text):
return re.sub(r"(\s)\11,", r"\1", text)
def loop_while(text):
if " " in text:
while " " in text:
text = text.replace(" ", " ")
return text
if __name__ == "__main__":
import timeit
callable_functions = [item for item in locals().items() if callable(item[1])]
for func_name, func in callable_functions:
elapsed_time = timeit.timeit(f"func_name(text)", globals=globals(), number=100000)
print(f"func_name: elapsed_time \nfunc(text)\n")
这个输出:
正则表达式:1.378352418 这是紧急广播系统的测试
loop_while: 0.15858950299999997 这是对紧急情况的考验 广播系统
那么测试一个新版本所需要的只是添加一个新功能。比如:
def split_join(text):
return " ".join(text.split())
现在输出:
正则表达式:1.378352418 这是紧急广播系统的测试
loop_while: 0.15858950299999997 这是紧急广播系统的测试
split_join:0.05700970800000005 这是紧急广播系统的测试
【讨论】:
【参考方案6】:这应该可行:
import timeit
def f(x,y):
return x*y
x = 5
y = 7
print(timeit.timeit(stmt='f(x,y)',
setup='from __main__ import f, x, y',
number=1000))
【讨论】:
这可能是与 lambda 方法一起使用的最简单/最短的方法。【参考方案7】:我更喜欢创建一个 static
类,其中所有数据都准备好在运行计时器之前被拾取。
另外注意,最好在函数中而不是在全局空间中进行测试运行,因为全局空间没有利用
FAST_LOAD
Why does Python code run faster in a function?
class Data(object):
"""Data Creation"""
x = [i for i in range(0, 10000)]
y = tuple([i for i in range(0, 10000)])
def __init__(self):
pass
import timeit
def testIterator(x):
for i in range(10000):
z = i
print timeit.timeit("testIterator(Data.x)", setup="from __main__ import testIterator, Data", number=50)
print timeit.timeit("testIterator(Data.y)", setup="from __main__ import testIterator, Data", number=50)
【讨论】:
【参考方案8】:这是一个如何在不调用全局变量的情况下划分计时例程的示例
def foo(a, b):
'''Do something to `a` and `b`'''
return a + b
def time_foo():
'''Create timer object simply without using global variables'''
import timeit
_foo = foo
a = 1
b = 2
# Get `Timer` oject, alternatively just get time with `timeit.timeit()`
t = timeit.Timer('_foo(a, b)', globals=locals())
return t
如果您想使用相同的 timeit
函数来为其他函数计时,您甚至可以概括这一点。这是您的示例main()
例程的示例:
def foo1(a, b):
'''Add `a` and `b`'''
return a + b
def foo2(a, b):
'''More math on `a` and `b`'''
return (a**2 * b)**2
def time_foo(func, **kwargs):
'''Create timer object simply without using global variables'''
import timeit
return timeit.timeit('func(**kwargs)', globals=locals())
def run():
'''Modify inputs to foo and see affect on execution time'''
a = 1
b = 2
for i in range(10):
# Update `a` and `b`
a += 1
b += 2
# Pass args to foo as **kwargs dict
print('foo1 time: ', time_foo(foo1, **'a':a, 'b':b))
print('foo2 time: ', time_foo(foo2, **'a':a, 'b':b))
return None
【讨论】:
【参考方案9】:我通常会创建一个额外的函数:
def f(x,y):
return x*y
v1 = 10
v2 = 20
def f_test():
f(v1,v2)
print(timeit.timeit("f_test()", setup="from __main__ import f_test"))
【讨论】:
仅当v1
和 v2
是全局变量时才有效。如果它们是本地的 f 函数不起作用【参考方案10】:
假设你的模块文件名是 test.py
# some pre-defined constants
A = 1
B = 2
# function that does something critical
def foo(n, m):
pass
# main program.... do something to A and B
for i in range(20):
pass
import timeit
t = timeit.Timer(stmt="test.foo(test.A, test.B)", setup="import test")
print t.timeit(5)
【讨论】:
【参考方案11】:您的函数需要在设置字符串中定义。一个很好的方法是在模块中设置你的代码,所以你必须这样做
t = timeit.Timer("foo(num1, num2)", "from myfile import foo")
t.timeit(5)
否则,您必须在 setup 语句中将所有设置定义为字符串。
setup = """
# some pre-defined constants
A = 1
B = 2
# function that does something critical
def foo(num1, num2):
# do something
# main program.... do something to A and B
for i in range(20):
# do something to A and B
# and update A and B during each iteration
"""
t = timeit.Timer("foo(num1, num2)", setup)
t.timeit(5)
我刚刚发现的一个很棒的事情是使用 cProfile 的 iPython 快捷方式。
def foo(x, y):
print x*y
%prun foo("foo", 100)
【讨论】:
【参考方案12】:代码 sn-ps 必须是自包含的 - 它们不能进行外部引用。您必须在语句字符串或设置字符串中定义您的值:
import timeit
setup = """
A = 1
B = 2
def foo(num1, num2):
pass
def mainprog():
global A,B
for i in range(20):
# do something to A and B
foo(A, B)
"""
t = timeit.Timer(stmt="mainprog()" setup=setup)
print(t.timeit(5))
更好的是,重写您的代码以不使用全局值。
【讨论】:
Lucas S. 的回答让人觉得有点不对劲。至少很尴尬。以上是关于使用 timeit.Timer() 时如何传递函数的参数的主要内容,如果未能解决你的问题,请参考以下文章