带参数的装饰器
Decorators with parameters
我有一个 collection 函数,它们具有(大部分)共享参数但不同的进程。我想使用装饰器将每个参数的描述添加到函数的 headline-level 文档字符串中。
我试图通过在 appender
中加入嵌套函数来模仿 this answer 中的结构,但失败了。我也试过 functools.partial
但有点不对劲。
我的尝试:
def appender(func, *args):
"""Appends additional parameter descriptions to func's __doc__."""
def _doc(func):
params = ''.join([defaultdocs[arg] for arg in args])
func.__doc__ += '\n' + params
return func
return _doc
defaultdocs = {
'a' :
"""
a : int, default 0
the first parameter
""",
'b' :
"""
b : int, default 1
the second parameter
"""
}
@appender('a')
def f(a):
"""Title-level docstring."""
return a
@appender('a', 'b')
def g(a, b):
"""Title-level docstring."""
return a + b
这失败了,我相信它失败了,因为传递给 appender
的第一个参数被解释为 func
。因此,当我查看 g
的结果文档字符串时,我得到:
print(g.__doc__)
Title-level docstring.
b : int, default 1
the second parameter
因为当我希望它成为 *args
的第一个元素时,'a'
又被解释为 'func'
。我该如何纠正?
想要的结果:
print(g.__doc__)
Title-level docstring.
a : int, default 0
the first parameter
b : int, default 1
the second parameter
发生这种情况是因为您传递的变量名称实际上被捕获到 func
参数中。
为了在 Python 中执行可调用装饰器,您需要对函数进行两次编码,使用 external 函数来接受装饰器参数和 internal 接受原函数的函数。可调用装饰器只是 return 其他装饰器的高阶函数。例如:
def appender(*args): # This is called when a decorator is called,
# e. g. @appender('a', 'b')
"""Appends additional parameter descriptions to func's __doc__."""
def _doc(func): # This is called when the function is about
# to be decorated
params = ''.join([defaultdocs[arg] for arg in args])
func.__doc__ += '\n' + params
return func
return _doc
外部 (appender
) 函数充当新装饰器的工厂,而 _doc
函数是一个实际的装饰器。总是这样传递:
- 将装饰器参数传递给外部函数
- 将原函数传递给内部函数
Python 看到后:
@appender('a', 'b')
def foo(): pass
...它会在引擎盖下做这样的事情:
foo = appender('a', 'b')(foo)
...扩展为:
decorator = appender('a', 'b')
foo = decorator(foo)
由于 Python 中作用域的工作方式,每个新 returned _doc
函数实例都将具有来自外部函数的自己的本地 args
值。
使用 inspect.signature
收集传递的函数参数的替代解决方案。
import inspect
import textwrap
def appender(defaultdocs):
def _doc(func):
params = inspect.signature(func).parameters
params = [param.name for param in params.values()]
params = ''.join([textwrap.dedent(defaultdocs[param])
for param in params])
func.__doc__ += '\n\nParameters\n' + 10 * '=' + params
return func
return _doc
示例:
# default docstrings for parameters that are re-used often
# class implementation not a good alternative in my specific case
defaultdocs = {
'a' :
"""
a : int, default 0
the first parameter""",
'b' :
"""
b : int, default 1
the second parameter"""
}
@appender
def f(a):
"""Title-level docstring."""
return a
@appender
def g(a, b):
"""Title-level docstring."""
return a + b
这会将 a
和 b
的描述附加到 g.__doc__
而无需在装饰器中指定它们:
help(g)
Help on function g in module __main__:
g(a, b)
Title-level docstring.
Parameters
==========
a : int, default 0
the first parameter
b : int, default 1
the second parameter
我有一个 collection 函数,它们具有(大部分)共享参数但不同的进程。我想使用装饰器将每个参数的描述添加到函数的 headline-level 文档字符串中。
我试图通过在 appender
中加入嵌套函数来模仿 this answer 中的结构,但失败了。我也试过 functools.partial
但有点不对劲。
我的尝试:
def appender(func, *args):
"""Appends additional parameter descriptions to func's __doc__."""
def _doc(func):
params = ''.join([defaultdocs[arg] for arg in args])
func.__doc__ += '\n' + params
return func
return _doc
defaultdocs = {
'a' :
"""
a : int, default 0
the first parameter
""",
'b' :
"""
b : int, default 1
the second parameter
"""
}
@appender('a')
def f(a):
"""Title-level docstring."""
return a
@appender('a', 'b')
def g(a, b):
"""Title-level docstring."""
return a + b
这失败了,我相信它失败了,因为传递给 appender
的第一个参数被解释为 func
。因此,当我查看 g
的结果文档字符串时,我得到:
print(g.__doc__)
Title-level docstring.
b : int, default 1
the second parameter
因为当我希望它成为 *args
的第一个元素时,'a'
又被解释为 'func'
。我该如何纠正?
想要的结果:
print(g.__doc__)
Title-level docstring.
a : int, default 0
the first parameter
b : int, default 1
the second parameter
发生这种情况是因为您传递的变量名称实际上被捕获到 func
参数中。
为了在 Python 中执行可调用装饰器,您需要对函数进行两次编码,使用 external 函数来接受装饰器参数和 internal 接受原函数的函数。可调用装饰器只是 return 其他装饰器的高阶函数。例如:
def appender(*args): # This is called when a decorator is called,
# e. g. @appender('a', 'b')
"""Appends additional parameter descriptions to func's __doc__."""
def _doc(func): # This is called when the function is about
# to be decorated
params = ''.join([defaultdocs[arg] for arg in args])
func.__doc__ += '\n' + params
return func
return _doc
外部 (appender
) 函数充当新装饰器的工厂,而 _doc
函数是一个实际的装饰器。总是这样传递:
- 将装饰器参数传递给外部函数
- 将原函数传递给内部函数
Python 看到后:
@appender('a', 'b')
def foo(): pass
...它会在引擎盖下做这样的事情:
foo = appender('a', 'b')(foo)
...扩展为:
decorator = appender('a', 'b')
foo = decorator(foo)
由于 Python 中作用域的工作方式,每个新 returned _doc
函数实例都将具有来自外部函数的自己的本地 args
值。
使用 inspect.signature
收集传递的函数参数的替代解决方案。
import inspect
import textwrap
def appender(defaultdocs):
def _doc(func):
params = inspect.signature(func).parameters
params = [param.name for param in params.values()]
params = ''.join([textwrap.dedent(defaultdocs[param])
for param in params])
func.__doc__ += '\n\nParameters\n' + 10 * '=' + params
return func
return _doc
示例:
# default docstrings for parameters that are re-used often
# class implementation not a good alternative in my specific case
defaultdocs = {
'a' :
"""
a : int, default 0
the first parameter""",
'b' :
"""
b : int, default 1
the second parameter"""
}
@appender
def f(a):
"""Title-level docstring."""
return a
@appender
def g(a, b):
"""Title-level docstring."""
return a + b
这会将 a
和 b
的描述附加到 g.__doc__
而无需在装饰器中指定它们:
help(g)
Help on function g in module __main__:
g(a, b)
Title-level docstring.
Parameters
==========
a : int, default 0
the first parameter
b : int, default 1
the second parameter