【问题标题】:how to pass parameters of a function when using timeit.Timer()使用 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 对于单行来说是可以的,但对于其他更好的方法,请检查此方法stackoverflow.com/questions/5478351/…
  • 在python3中有一个非常简单的解决方案,你可以通过在timeit调用中添加globals=globals()参数在全局命名空间中运行表达式:stackoverflow.com/a/51913199/2925963

标签: python timer


【解决方案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)\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")
    

    这个输出:

    正则表达式: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_LOADWhy 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"))
          

          【讨论】:

          • 仅当 v1v2 是全局变量时才有效。如果它们是本地的 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. 的回答让人觉得有点不对劲。至少很尴尬。
              猜你喜欢
              • 2023-01-31
              • 2023-01-06
              • 1970-01-01
              • 1970-01-01
              • 2021-04-30
              • 2015-07-08
              • 2021-03-17
              • 2011-12-19
              • 2014-06-04
              相关资源
              最近更新 更多