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

时间:2015-05-19 08:26:15

标签: python decorator python-2.6 python-decorators

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

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
    # ...

似乎得到的论点是“世界”,应该实际传递给get_text

反转装饰器的顺序没有帮助。有没有什么可以同时在带有和不带参数的一般装饰器中实现?

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

我正在使用python 2.6

1 个答案:

答案 0 :(得分: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

*args函数签名中的**kwwrapper语法分别捕获元组和字典中的任何位置和关键字参数。 调用表达式中非常相似(相关)的语法接受序列或字典,并将那些内容作为位置或关键字参数应用于被调用的对象。这整齐地传递了从包装器到包装的任意数量的参数。

这对于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'