【问题标题】:Attempt at decorators with multiple order python function尝试使用多阶 python 函数的装饰器
【发布时间】:2018-04-11 17:12:35
【问题描述】:

我目前正在使用 python 中的多个订单函数,作为练习,我想尝试使用函数装饰器。在这个例子中,我有一个简单的计数器,并希望使用 datetime 来估计计算机计算到给定代码的某个值所需的时间:

from datetime import datetime

def elapsedTime(f):
    time1=datetime.now()
    def runFunc():
        return f()
    time2=datetime.now()
    return time2-time1

@elapsedTime
def count(n):
    out=0
    for i in range(n):
        out += 1
    return out

out, deltaT= count(50000)
print("count(50000) returns:", out)
print("elapsed time (sec): ", deltaT.total_seconds())
print("type of deltaT: ", type(deltaT))

我期望的输出是:

count(50000) returns: 50000
elapsed time (sec): (calculated value)
type of deltaT:  <class 'datetime.timedelta'>

但是,每当我尝试运行此代码时,我都会在第 17 行(我在此处调用 count(50000))收到错误消息:

TypeError: 'datetime.timedelta' object is not callable

我对此有点迷茫,希望有人能指出错误发生的位置。

【问题讨论】:

    标签: python python-3.x datetime typeerror python-decorators


    【解决方案1】:

    无论装饰器 (elapsedTime()) 返回什么,都将用于替换被装饰的函数。因此,如果您仍然希望 count() 工作,最好是一个可调用对象。

    那是因为@decorator 语法实际上只是一点语法糖;无论您在哪个函数上使用该语法,都会执行 def function_name(...): 块,而不是常规的 function_name = &lt;function object&gt; 分配,而是调用和使用装饰器,就像调用了 function_name = decorator(&lt;function_object(&gt;) 一样。

    然而,你的装饰器只是定时创建你的包装函数,并没有真正返回一个新的函数对象。它返回创建包装函数的 timedelta 结果。

    将您的计时器放入runFunc() 包装函数,然后返回该包装器(不要调用它,只需返回它)。这样,包装函数将替换原来的 count() 函数对象。

    接下来,确保您的包装函数可以接受任何参数,并将这些参数传递给包装函数。我使用*args, **kwargs 将扔给它的任何东西都放在包装器中,并将其原封不动地传递给原始函数:

    def elapsedTime(f):
        def runFunc(*args, **kwargs):
            time1 = datetime.now()
            result = f(*args, **kwargs)
            time2 = datetime.now()
            return result, (time2 - time1)
        return runFunc
    

    请注意,我还更改了 runFunc() 包装器返回的内容。它返回调用原始函数的结果,时间增量。因为这就是您的代码所期望的。

    上面的工作现在是这样的:

    >>> from datetime import datetime
    >>> def elapsedTime(f):
    ...     def runFunc(*args, **kwargs):
    ...         time1 = datetime.now()
    ...         result = f(*args, **kwargs)
    ...         time2 = datetime.now()
    ...         return result, (time2 - time1)
    ...     return runFunc
    ...
    >>> @elapsedTime
    ... def count(n):
    ...     out=0
    ...     for i in range(n):
    ...         out += 1
    ...     return out
    ...
    >>> out, deltaT = count(50000)
    >>> print("count(50000) returns:", out)
    count(50000) returns: 50000
    >>> print("elapsed time (sec): ", deltaT.total_seconds())
    elapsed time (sec):  0.006237
    >>> print("type of deltaT: ", type(deltaT))
    type of deltaT:  <class 'datetime.timedelta'>
    

    【讨论】:

      猜你喜欢
      • 2018-03-07
      • 2011-12-20
      • 1970-01-01
      • 2014-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-18
      相关资源
      最近更新 更多