关于python:使用timeit.timer()时如何传递函数的参数

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

这是一个简单程序的大纲

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 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没有定义"……有人能帮我吗?谢谢!


函数可以使用timeit中的参数。如果这些参数是使用闭包创建的,那么我们可以通过将它们包装在另一个函数中来添加这些行为。

1
2
3
4
5
6
7
8
9
10
11
12
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而不是显式闭包声明

1
2
3
4
5
6
7
8
9
10
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)


假设您的模块文件名是test.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 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)


代码段必须是自包含的-它们不能进行外部引用。必须在语句字符串或设置字符串中定义值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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))

更好的是,重写代码以不使用全局值。


您的函数需要在设置字符串中定义。实现这一点的一个好方法是在模块中设置代码,因此您只需

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

否则,您必须将所有设置定义为SETUP语句中的字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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)

我刚刚发现的一个很棒的东西是ipython使用cprofile的快捷方式。

1
2
3
4
def foo(x, y):
    print x*y

%prun foo("foo", 100)

我通常会创建一个额外的函数:

1
2
3
4
5
6
7
8
9
10
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"))


有一个更简单的解决方案(至少对于Python3),您可以使代码在当前全局命名空间中执行:

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

https://docs.python.org/3/library/timeit.html示例我知道全局参数不是首选,但是如果您只是制作一个快速脚本来检查某些东西,我认为这是最简单的实现。


下面是一个如何划分计时例程而不调用全局的示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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()例程的一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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

今天我在python3.7中玩计时,试图将函数和变量传递到计时器中。这就是我想到的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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}
{func(text)}
"
)

此输出:

regex: 1.378352418
This is a test of the emergency broadcast system

loop_while: 0.15858950299999997
This is a test of the emergency
broadcast system

然后测试一个新版本所需要的就是添加一个新的函数。类似:

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

现在它输出:

regex: 1.378352418
This is a test of the emergency broadcast system

loop_while: 0.15858950299999997
This is a test of the emergency broadcast system

split_join: 0.05700970800000005
This is a test of the emergency broadcast system


这应该有效:

1
2
3
4
5
6
7
8
9
10
11
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))

我更喜欢创建一个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?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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)