Python - 修饰器应用程序的正确顺序

时间:2010-10-06 16:52:24

标签: python decorator

我正在装饰一个函数:

def some_abstract_decorator(func):
    @another_lower_level_decorator
    def wrapper(*args, **kwargs):
        # ... details omitted
        return func(*args, **kwargs)
    return wrapper

这就是你所期望的(应用一个低级装饰器,然后做更多的东西。我的问题是我现在想要使用functools.wraps而我不知道在哪里放它。这是我的猜测,但我不知道它是否会产生意想不到的后果。

def some_abstract_decorator(func):
    @wraps(func)
    @another_lower_level_decorator
    def wrapper(*args, **kwargs):
        # ... details omitted
        return func(*args, **kwargs)
    return wrapper

(我当然也在wraps内部应用another_lower_level_decorator

3 个答案:

答案 0 :(得分:2)

没错。这种方式的工作方式是

  • wrapper已定义。它使用其参数调用func
  • 调用
  • another_lower_level_decorator,其中wrapper为其参数。它返回的函数成为wrapper的新值。
  • 调用
  • wraps(func)来创建一个将应用名称/ docstring / etc的包装器。 func对任何被调用的函数。
  • wraps(func)的返回值,即生成的包装函数,将传递当前值wrapper。记住,这是another_lower_level_decorator的返回值。
  • wraps(func)(wrapper)成为wrapper
  • 的新值
  • 该值由some_abstract_decorator返回,使该函数适合用作装饰器。
无论如何,或者那是有效的。我认为在实践中wrapper只会被重新分配一次。

答案 1 :(得分:2)

尝试一下:

from functools import wraps    

def another_lower_level_decorator(func):
    @wraps( func )
    def wrapped(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapped

def some_abstract_decorator(func):
    @wraps(func)
    @another_lower_level_decorator
    def wrapper(*args, **kwargs):
        # ... details omitted
        return func(*args, **kwargs)
    return wrapper


@some_abstract_decorator
def test():
    """ This is a docstring that should be on the decorated function """
    pass

help(test)

打印:

Help on function test in module __main__:

test(*args, **kwargs)
    This is a docstring that should be on the decorated function

正如你所看到的那样有效!文档字符串在那里,并指定了名称。

但这也是一样的:

def some_abstract_decorator(func):
    @another_lower_level_decorator
    @wraps(func)
    def wrapper(*args, **kwargs):
        # ... details omitted
        return func(*args, **kwargs)
    return wrapper

wraps只修复了文档字符串/名称。只要所有装饰器都使用wraps,您应用它的顺序无关紧要

顺便说一下,有a much cooler decorator library

from decorator import decorator

@decorator
def another_decorator(func, *args, **kwargs):
    return func(*args, **kwargs)

@decorator
@another_decorator
def some_abstract_decorator(func, *args, **kwargs):
    # ... details omitted
    return func(*args, **kwargs)


@some_abstract_decorator
def test(x):
    """ this is a docstring that should be on the decorated function """
    pass

答案 2 :(得分:1)

是的,这对我来说是正确的。 @another_lower_level_decorator将返回一个函数,@wraps将换行,以便它与func具有相同的名称。