Python - 装饰器应用的正确顺序
我正在装饰一个函数,像这样:
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
(当然,我也会在 another_lower_level_decorator
里面使用 wraps
)
3 个回答
1
是的,我觉得这样没问题。@another_lower_level_decorator
会返回一个函数,然后 @wraps
会把这个函数包装起来,这样它的名字就和 func
一样了。
2
没错,这个过程是这样的:
wrapper
被定义了。它会用自己的参数去调用func
。another_lower_level_decorator
被调用,传入wrapper
作为参数。它返回的函数会成为新的wrapper
的值。- 接着调用
wraps(func)
,这个函数会创建一个包装器,用来把func
的名字、文档字符串等信息应用到它所调用的任何函数上。 wraps(func)
的返回值,也就是生成的包装函数,会接收当前的wrapper
值。记住,这个值是来自another_lower_level_decorator
的返回值。wraps(func)(wrapper)
变成了新的wrapper
的值。- 这个值会被
some_abstract_decorator
返回,使得这个函数可以用作装饰器。
大致就是这样。我觉得在实际操作中,wrapper
只会被重新赋值一次。
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
,那么你应用它的顺序就不重要了。
顺便说一下,这里有一个更酷的装饰器库:点击查看:
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