假设我已经编写了一个装饰器,它可以做一些非常通用的事情。例如,它可以将所有参数转换为特定类型、执行日志记录、实现制表等。
这里有一个例子:
def args_as_ints(f):
def g(*args, **kwargs):
args = [int(x) for x in args]
kwargs = dict((k, int(v)) for k, v in kwargs.items())
return f(*args, **kwargs)
return g
@args_as_ints
def funny_function(x, y, z=3):
"""Computes x*y + 2*z"""
return x*y + 2*z
>>> funny_function("3", 4.0, z="5")
22
目前一切顺利。然而,还有一个问题。修饰函数不保留原始函数的文档:
>>> help(funny_function)
Help on function g in module __main__:
g(*args, **kwargs)
幸运的是,有一个解决办法:
def args_as_ints(f):
def g(*args, **kwargs):
args = [int(x) for x in args]
kwargs = dict((k, int(v)) for k, v in kwargs.items())
return f(*args, **kwargs)
g.__name__ = f.__name__
g.__doc__ = f.__doc__
return g
@args_as_ints
def funny_function(x, y, z=3):
"""Computes x*y + 2*z"""
return x*y + 2*z
这一次,函数名和文档是正确的:
>>> help(funny_function)
Help on function funny_function in module __main__:
funny_function(*args, **kwargs)
Computes x*y + 2*z
但是仍然存在一个问题: 函数签名是错误的。信息“ * args,* * kwargs”几乎是无用的。
我能想到两个简单但有缺陷的解决办法:
1——在 docstring 中包含正确的签名:
def funny_function(x, y, z=3):
"""funny_function(x, y, z=3) -- computes x*y + 2*z"""
return x*y + 2*z
这是不好的,因为复制。在自动生成的文档中仍然不能正确显示签名。更新函数很容易忘记更改 docstring,或者输入错误。[ 是的,我知道 docstring 已经复制了函数体。请忽略这一点; fun _ function 只是一个随机示例。]
2——不使用装饰工具,也不对每个特定的签名使用特殊用途的装饰工具:
def funny_functions_decorator(f):
def g(x, y, z=3):
return f(int(x), int(y), z=int(z))
g.__name__ = f.__name__
g.__doc__ = f.__doc__
return g
对于具有相同签名的一组函数,这种方法可以很好地工作,但通常是无用的。正如我在开始时所说的,我希望能够完全通用地使用装饰器。
我正在寻找一个完全通用和自动的解决方案。
所以问题是: 有没有一种方法可以在创建修饰的函数签名之后编辑它?
否则,我是否可以编写一个装饰器来提取函数签名并在构造装饰函数时使用该信息而不是“ * kwargs,* * kwargs”?我如何提取这些信息?我应该如何构造修饰函数——使用 exec?
还有别的办法吗?