将 functools.wraps 应用于嵌套包装器
Applying functools.wraps to nested wrappers
我有一个接受参数的基本装饰器,但它也是由其他装饰器构建的。我似乎无法确定将 functools.wraps 放在哪里以保留装饰函数的完整签名。
import inspect
from functools import wraps
# Base decorator
def _process_arguments(func, *indices):
""" Apply the pre-processing function to each selected parameter """
@wraps(func)
def wrap(f):
@wraps(f)
def wrapped_f(*args):
params = inspect.getargspec(f)[0]
args_out = list()
for ind, arg in enumerate(args):
if ind in indices:
args_out.append(func(arg))
else:
args_out.append(arg)
return f(*args_out)
return wrapped_f
return wrap
# Function that will be used to process each parameter
def double(x):
return x * 2
# Decorator called by end user
def double_selected(*args):
return _process_arguments(double, *args)
# End-user's function
@double_selected(2, 0)
def say_hello(a1, a2, a3):
""" doc string for say_hello """
print('{} {} {}'.format(a1, a2, a3))
say_hello('say', 'hello', 'arguments')
这段代码的结果应该是并且是:
saysay hello argumentsarguments
然而,运行 对 say_hello 的帮助给了我:
say_hello(*args, **kwargs)
doc string for say_hello
除了参数名称之外的所有内容都被保留。
看来我只需要在某处添加另一个@wraps(),但是在哪里呢?
我试验过这个:
>>> from functools import wraps
>>> def x(): print(1)
...
>>> @wraps(x)
... def xyz(a,b,c): return x
>>> xyz.__name__
'x'
>>> help(xyz)
Help on function x in module __main__:
x(a, b, c)
据我所知,这与 wraps
本身无关,而是与 help
有关的问题。事实上,因为 help
检查你的对象以提供信息,包括 __doc__
和其他属性,这就是你得到这种行为的原因,尽管你的包装函数有不同的参数列表。虽然,wraps
不会自动更新(参数列表)它真正更新的是这个元组和 __dict__
技术上是对象命名空间:
WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',
'__annotations__')
WRAPPER_UPDATES = ('__dict__',)
如果您不确定 wraps
的工作原理,阅读标准库中的源代码可能会有所帮助:functools.py
.
It seems like I just need to add another @wraps() somewhere, but where?
不,您不需要在代码中添加另一个 wraps
,正如我上面所说的那样,help
通过检查您的对象来工作。函数的参数与代码对象 (__code__
) 相关联,因为你的函数的参数在该对象中是 stored/represented,wraps
无法更新包装器的参数,使其与包装函数一样(继续上面的例子):
>>> xyz.__code__.co_varnames
>>> xyz.__code__.co_varnames = x.__code__.co_varnames
AttributeError: readonly attribute
如果 help
显示函数 xyz
具有此参数列表 ()
而不是 (a, b, c)
那么这显然是错误的!同样适用于wraps
,把wrapper的参数列表改成wrapped,会很麻烦!所以这根本不应该是一个问题。
>>> @wraps(x, ("__code__",))
... def xyz(a,b,c): pass
...
>>> help(xyz)
Help on function xyz in module __main__:
xyz()
但是xyz()
returnsx()
:
>>> xyz()
1
有关其他参考资料,请查看此问题或 Python 文档
What does functools.wraps do?
direprobs 是正确的,因为再多的 functools 包装也无法将我带到那里。 bravosierra99 向我指出了一些相关的例子。但是,我找不到在嵌套装饰器上保留签名的单个示例,其中外部装饰器采用参数。
带参数的装饰器 comments on Bruce Eckel's post 给了我实现我想要的结果的最大提示。
关键是从我的 _process_arguments 函数中删除中间函数,并将其参数放在下一个嵌套函数中。现在对我来说有点意义......但它有效:
import inspect
from decorator import decorator
# Base decorator
def _process_arguments(func, *indices):
""" Apply the pre-processing function to each selected parameter """
@decorator
def wrapped_f(f, *args):
params = inspect.getargspec(f)[0]
args_out = list()
for ind, arg in enumerate(args):
if ind in indices:
args_out.append(func(arg))
else:
args_out.append(arg)
return f(*args_out)
return wrapped_f
# Function that will be used to process each parameter
def double(x):
return x * 2
# Decorator called by end user
def double_selected(*args):
return _process_arguments(double, *args)
# End-user's function
@double_selected(2, 0)
def say_hello(a1, a2,a3):
""" doc string for say_hello """
print('{} {} {}'.format(a1, a2, a3))
say_hello('say', 'hello', 'arguments')
print(help(say_hello))
结果:
saysay hello argumentsarguments
Help on function say_hello in module __main__:
say_hello(a1, a2, a3)
doc string for say_hello
我有一个接受参数的基本装饰器,但它也是由其他装饰器构建的。我似乎无法确定将 functools.wraps 放在哪里以保留装饰函数的完整签名。
import inspect
from functools import wraps
# Base decorator
def _process_arguments(func, *indices):
""" Apply the pre-processing function to each selected parameter """
@wraps(func)
def wrap(f):
@wraps(f)
def wrapped_f(*args):
params = inspect.getargspec(f)[0]
args_out = list()
for ind, arg in enumerate(args):
if ind in indices:
args_out.append(func(arg))
else:
args_out.append(arg)
return f(*args_out)
return wrapped_f
return wrap
# Function that will be used to process each parameter
def double(x):
return x * 2
# Decorator called by end user
def double_selected(*args):
return _process_arguments(double, *args)
# End-user's function
@double_selected(2, 0)
def say_hello(a1, a2, a3):
""" doc string for say_hello """
print('{} {} {}'.format(a1, a2, a3))
say_hello('say', 'hello', 'arguments')
这段代码的结果应该是并且是:
saysay hello argumentsarguments
然而,运行 对 say_hello 的帮助给了我:
say_hello(*args, **kwargs)
doc string for say_hello
除了参数名称之外的所有内容都被保留。
看来我只需要在某处添加另一个@wraps(),但是在哪里呢?
我试验过这个:
>>> from functools import wraps
>>> def x(): print(1)
...
>>> @wraps(x)
... def xyz(a,b,c): return x
>>> xyz.__name__
'x'
>>> help(xyz)
Help on function x in module __main__:
x(a, b, c)
据我所知,这与 wraps
本身无关,而是与 help
有关的问题。事实上,因为 help
检查你的对象以提供信息,包括 __doc__
和其他属性,这就是你得到这种行为的原因,尽管你的包装函数有不同的参数列表。虽然,wraps
不会自动更新(参数列表)它真正更新的是这个元组和 __dict__
技术上是对象命名空间:
WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',
'__annotations__')
WRAPPER_UPDATES = ('__dict__',)
如果您不确定 wraps
的工作原理,阅读标准库中的源代码可能会有所帮助:functools.py
.
It seems like I just need to add another @wraps() somewhere, but where?
不,您不需要在代码中添加另一个 wraps
,正如我上面所说的那样,help
通过检查您的对象来工作。函数的参数与代码对象 (__code__
) 相关联,因为你的函数的参数在该对象中是 stored/represented,wraps
无法更新包装器的参数,使其与包装函数一样(继续上面的例子):
>>> xyz.__code__.co_varnames
>>> xyz.__code__.co_varnames = x.__code__.co_varnames
AttributeError: readonly attribute
如果 help
显示函数 xyz
具有此参数列表 ()
而不是 (a, b, c)
那么这显然是错误的!同样适用于wraps
,把wrapper的参数列表改成wrapped,会很麻烦!所以这根本不应该是一个问题。
>>> @wraps(x, ("__code__",))
... def xyz(a,b,c): pass
...
>>> help(xyz)
Help on function xyz in module __main__:
xyz()
但是xyz()
returnsx()
:
>>> xyz()
1
有关其他参考资料,请查看此问题或 Python 文档
What does functools.wraps do?
direprobs 是正确的,因为再多的 functools 包装也无法将我带到那里。 bravosierra99 向我指出了一些相关的例子。但是,我找不到在嵌套装饰器上保留签名的单个示例,其中外部装饰器采用参数。
带参数的装饰器 comments on Bruce Eckel's post 给了我实现我想要的结果的最大提示。
关键是从我的 _process_arguments 函数中删除中间函数,并将其参数放在下一个嵌套函数中。现在对我来说有点意义......但它有效:
import inspect
from decorator import decorator
# Base decorator
def _process_arguments(func, *indices):
""" Apply the pre-processing function to each selected parameter """
@decorator
def wrapped_f(f, *args):
params = inspect.getargspec(f)[0]
args_out = list()
for ind, arg in enumerate(args):
if ind in indices:
args_out.append(func(arg))
else:
args_out.append(arg)
return f(*args_out)
return wrapped_f
# Function that will be used to process each parameter
def double(x):
return x * 2
# Decorator called by end user
def double_selected(*args):
return _process_arguments(double, *args)
# End-user's function
@double_selected(2, 0)
def say_hello(a1, a2,a3):
""" doc string for say_hello """
print('{} {} {}'.format(a1, a2, a3))
say_hello('say', 'hello', 'arguments')
print(help(say_hello))
结果:
saysay hello argumentsarguments
Help on function say_hello in module __main__:
say_hello(a1, a2, a3)
doc string for say_hello