【问题标题】:Using decorators with and without arguments simultaneously同时使用带参数和不带参数的装饰器
【发布时间】:2015-05-19 08:26:14
【问题描述】:

我正在尝试测量这样的修饰函数的运行时间:

import time

def timing_function(some_function):
    """Outputs the time a function takes to execute."""
    def wrapper():
        t1 = time.time()
        some_function()
        time.sleep(1)
        t2 = time.time()
        return "Time it took to run the function " + some_function.__name__ + " is " + str((t2-t1)) + "\n"
    return wrapper

def tags(tag_name):
    def tags_decorator(my_func):
        """Adds tags to a string."""
        def func_wrapper(name):
            return "<{0}>{1}</{0}>".format(tag_name, my_func(name))
        return func_wrapper
    return tags_decorator

@timing_function
@tags("p")
def get_text(name):
    return "Hello "+name

那我试试

print get_text("World")

但是得到

TypeError: wrapper() takes no arguments (1 given)

有趣的是,我添加了一段代码来检查我给 wrapper() 的参数是什么:

def wrapper(*args):
    print "args", args
    # ...

好像得到了参数“World”,其实应该是传给get_text的。

颠倒装饰器的顺序并没有帮助。我可以做些什么来同时实现带参数和不带参数的一般装饰器?

当然,我可以将两个代码合并到一个包装器中,但这不是我想要的......

我正在使用 python 2.6

【问题讨论】:

  • 这段代码的缩进好像被拍到了——也许你可以调整一下?
  • @PM2Ring:不,因为这是关于将参数从装饰器包装器传递到装饰器函数。 get_text() 函数接受一个参数,timing_function 生成的包装器根本不会传递它。

标签: python decorator python-2.6 python-decorators


【解决方案1】:

您需要调整 wrapper() 函数以接受任意数量的参数(位置和关键字),然后将这些参数传递到包装函数:

def timing_function(some_function):
    """Outputs the time a function takes to execute."""
    def wrapper(*args, **kw):
        t1 = time.time()
        some_function(*args, **kw)
        time.sleep(1)
        t2 = time.time()
        return "Time it took to run the function " + some_function.__name__ + " is " + str((t2-t1)) + "\n"
    return wrapper

wrapper 函数签名中的*args**kw 语法分别捕获元组和字典中的任何位置和关键字参数。 调用表达式中非常相似(相关)的语法采用序列或字典,并将它们的内容作为位置或关键字参数应用到被调用的对象。这巧妙地将任意数量的参数从包装器传递到包装器。

这不是您的 tags 装饰器的问题,因为它的包装器采用与包装函数完全相同的参数。这将装饰器限制为只接受一个参数的函数(这也很好)。

考虑到这个装饰器忽略了包装函数的返回值;相反,您将返回计时结果。您可能仍希望使返回值可访问(可能通过返回 (return_value, timing_info) 的元组)。

演示:

>>> import time
>>> def timing_function(some_function):
...     """Outputs the time a function takes to execute."""
...     def wrapper(*args, **kw):
...         t1 = time.time()
...         some_function(*args, **kw)
...         time.sleep(1)
...         t2 = time.time()
...         return "Time it took to run the function " + some_function.__name__ + " is " + str((t2-t1)) + "\n"
...     return wrapper
... 
>>> def tags(tag_name):
...     def tags_decorator(my_func):
...         """Adds tags to a string."""
...         def func_wrapper(name):
...             return "<{0}>{1}</{0}>".format(tag_name, my_func(name))
...         return func_wrapper
...     return tags_decorator
... 
>>> @timing_function
... @tags("p")
... def get_text(name):
...     return "Hello "+name
... 
>>> get_text('World')
'Time it took to run the function func_wrapper is 1.00514888763\n'

【讨论】:

    猜你喜欢
    • 2018-01-07
    • 2022-12-28
    • 2018-09-17
    • 2020-06-24
    • 1970-01-01
    • 1970-01-01
    • 2010-11-20
    • 2014-07-21
    相关资源
    最近更新 更多