与以下密切相关:How can I programmatically change the argspec of a function in a python decorator?
装饰器模块提供了制作装饰器函数的方法,该函数保留装饰函数的 argspec。
如果我定义了一个不用作装饰器的函数,有没有办法复制另一个函数的 argspec?
示例用例:
class Blah(object):
def foo(self, *args, **kwargs):
""" a docstr """
result = bar(*args, **kwargs)
result = result**2 # just so it's clear we're doing something extra here...
return result
def bar(x, y, z=1, q=2):
""" a more useful docstr, saying what x,y,z,q do """
return x+y*z+q
我想让 foo
的 argspec 看起来像 bar
的,但源代码保持不变(即 inspect.getsource(foo)
仍然会显示 result
垃圾)。这样做的主要目的是获取 sphinx 文档和 ipython 的交互式帮助以显示适当的参数。
正如另一个问题的答案所说,decorator package展示了一种方法来做到这一点,但我迷失在该代码的核心中。看起来 decorator
包正在重新编译源代码,或者类似的东西。我曾希望有一种更简单的方法,例如像 foo.argspec = bar.argspec
这样的东西是可能的。
最佳答案
装饰器只是一个函数,它与另一个函数一起做某事。因此,从技术上讲,您可以将所需的代码直接放在 foo
方法下面,然后,从技术上讲,您可以在不使用装饰器的情况下更改 foo
,但这会很糟糕乱七八糟。
做你想做的最简单的方法是制作一个装饰器,它接受第二个函数(在本例中为 bar
)作为参数,这样它就知道要复制哪个签名。类代码将类似于:
class Blah(object):
@copy_argspec(bar)
def foo(self, *args, **kwargs):
""" a docstr """
result = bar(*args, **kwargs)
result = result**2 # just so it's clear we're doing something extra here...
return result
你必须在课前而不是课后定义bar
。
.
.
.
. . . 时间流逝。 . .
.
.
.
好吧,幸运的是我找到了一个我可以适应的旧装饰器。
help(Blah.foo)
在装饰之前看起来像这样:
Help on method foo in module __main__:
foo(self, *args, **kwargs) unbound __main__.Blah method
a docstr
装饰后是这样的:
Help on method foo in module __main__:
foo(self, x, y, z=1, q=2) unbound __main__.Blah method
a more useful docstr, saying what x,y,z,q do
这是我使用的装饰器:
import inspect
class copy_argspec(object):
"""
copy_argspec is a signature modifying decorator. Specifically, it copies
the signature from `source_func` to the wrapper, and the wrapper will call
the original function (which should be using *args, **kwds). The argspec,
docstring, and default values are copied from src_func, and __module__ and
__dict__ from tgt_func.
"""
def __init__(self, src_func):
self.argspec = inspect.getargspec(src_func)
self.src_doc = src_func.__doc__
self.src_defaults = src_func.func_defaults
def __call__(self, tgt_func):
tgt_argspec = inspect.getargspec(tgt_func)
need_self = False
if tgt_argspec[0][0] == 'self':
need_self = True
name = tgt_func.__name__
argspec = self.argspec
if argspec[0][0] == 'self':
need_self = False
if need_self:
newargspec = (['self'] + argspec[0],) + argspec[1:]
else:
newargspec = argspec
signature = inspect.formatargspec(
formatvalue=lambda val: "",
*newargspec
)[1:-1]
new_func = (
'def _wrapper_(%(signature)s):\n'
' return %(tgt_func)s(%(signature)s)' %
{'signature':signature, 'tgt_func':'tgt_func'}
)
evaldict = {'tgt_func' : tgt_func}
exec new_func in evaldict
wrapped = evaldict['_wrapper_']
wrapped.__name__ = name
wrapped.__doc__ = self.src_doc
wrapped.func_defaults = self.src_defaults
wrapped.__module__ = tgt_func.__module__
wrapped.__dict__ = tgt_func.__dict__
return wrapped
关于python - 如何以编程方式更改 python 装饰器中函数 *not* 的 argspec?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18625510/