同时使用带参数和不带参数的装饰器

Using decorators with and without arguments simultaneously

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

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

您需要调整您的 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值;您正在 return 计算计时结果。您可能仍然希望使 return 值可访问(可能通过 returning 一个 (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'