简体   繁体   English

使用 timeit.Timer() 时如何传递函数的参数

[英]how to pass parameters of a function when using timeit.Timer()

This is the outline of a simple program这是一个简单程序的概要

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

I just keep getting "global name foo is not defined"..... Can anyone help me on this?我只是不断收到“未定义全局名称 foo”..... 任何人都可以帮助我吗? Thanks!谢谢!

The functions can use arguments in timeit if these are created using closures, we can add this behaviours by wrapping them in another function.如果这些函数是使用闭包创建的,则函数可以在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))

or shorter, we can use functools.partial instead of explicit closures declaration或者更短,我们可以使用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))

EDIT using lambda, thanks @jupiterbjy使用 lambda 编辑,谢谢@jupiterbjy

we can use lambda function without parameters instead of functools library我们可以使用不带参数的 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))

The code snippets must be self-contained - they cannot make external references.代码片段必须是自包含的 - 它们不能进行外部引用。 You must define your values in the statement-string or setup-string:您必须在语句字符串或设置字符串中定义您的值:

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

Better yet, rewrite your code to not use global values.更好的是,重写您的代码以不使用全局值。

Supposing that your module filename is test.py假设你的模块文件名是 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)

I usually create an extra function:我通常会创建一个额外的功能:

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

Your function needs to be define in the setup string.您的函数需要在设置字符串中定义。 A good way to do this is by setting up your code in a module, so you simple have to do一个很好的方法是在模块中设置你的代码,所以你很简单

t = timeit.Timer("foo(num1, num2)", "from myfile import foo")
t.timeit(5)

Otherwise, you'll have to define all of the setup as a string inside the setup statement.否则,您必须在 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)

Something awesome I just found out about is a shortcut for iPython that uses cProfile.我刚刚发现的一些很棒的东西是使用 cProfile 的 iPython 的快捷方式。

def foo(x, y):
    print x*y

%prun foo("foo", 100)

There is a much simpler solution (at least for Python 3), you can cause the code to be executed within your current global namespace:有一个更简单的解决方案(至少对于 Python 3),您可以使代码在您当前的全局命名空间中执行:

t = timeit.Timer(stmt="foo(num1,num2)", globals=globals())

https://docs.python.org/3/library/timeit.html#examples I know globals are not preferred, but if you are just making a quick script to check something I think this is the easiest implementation. https://docs.python.org/3/library/timeit.html#examples我知道全局变量不是首选,但是如果您只是制作一个快速脚本来检查某些内容,我认为这是最简单的实现。

Another option is to bind the function to its arguments via functools (similar to std::bind).另一种选择是通过functools将函数绑定到它的参数(类似于 std::bind)。 Then you don't need to pass arguments to timeit, the callable returned by functool.partial takes care of that:然后您不需要将参数传递给 timeit,由functool.partial返回的可调用对象负责处理:

    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)

Here is an example of how to compartmentalize the timing routine, without calling globals这是一个如何划分计时例程而不调用全局变量的示例

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

You could even generalize this if you wanted to use the same timeit function to time other functions.如果您想使用相同的timeit函数为其他函数计时,您甚至可以对此进行概括。 Here is an example with your example main() routine:这是您的示例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

I prefer creating a static class with all the Data ready to be picked up prior of running the timer.我更喜欢创建一个static类,在运行计时器之前准备好获取所有数据。

Another note, it is better to do test runs in function rather then in the global space, as the global space isn't taking advantage of FAST_LOAD Why does Python code run faster in a function?另一个注意事项,最好在函数中而不是在全局空间中进行测试运行,因为全局空间没有利用FAST_LOAD 为什么 Python 代码在函数中运行得更快?

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)

This should work:这应该有效:

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

I was playing around with timing in Python 3.7 today and trying to pass functions and variables into the timer.我今天在 Python 3.7 中玩弄计时,并尝试将函数和变量传递给计时器。 This is what I came up with.这就是我想出的。

import re

text = "This         is      a  test of the      emergency broadcast       system"

def regex(text):
    return re.sub(r"(\s)\1{1,}", 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} \n{func(text)}\n")

This outputs:这输出:

regex: 1.378352418正则表达式:1.378352418
This is a test of the emergency broadcast system这是对紧急广播系统的测试

loop_while: 0.15858950299999997 loop_while: 0.15858950299999997
This is a test of the emergency broadcast system这是对紧急广播系统的测试

Then all it takes to test a new version is adding in a new function.然后,测试新版本所需要做的就是添加新功能。 Something like:就像是:

def split_join(text):
    return " ".join(text.split())

Now it outputs:现在它输出:

regex: 1.378352418正则表达式:1.378352418
This is a test of the emergency broadcast system这是对紧急广播系统的测试

loop_while: 0.15858950299999997 loop_while: 0.15858950299999997
This is a test of the emergency broadcast system这是对紧急广播系统的测试

split_join: 0.05700970800000005 split_join: 0.05700970800000005
This is a test of the emergency broadcast system这是对紧急广播系统的测试

You have to create the variable within the setup string.您必须在设置字符串中创建变量。 Here I import the function, and create one of the variables that i pass to it.在这里,我导入函数,并创建传递给它的变量之一。 I also set one of the variables by casting it to the stmt string我还通过将其转换为 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))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM