假设我写了一个装饰器,它做了一些非常通用的事情。例如,它可能会将所有参数转换为特定类型、执行日志记录、实现记忆等。
这是一个例子:
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 -- 在文档字符串中包含正确的签名:
def funny_function(x, y, z=3):
"""funny_function(x, y, z=3) -- computes x*y + 2*z"""
return x*y + 2*z
由于重复,这很糟糕。签名仍不会在自动生成的文档中正确显示。更新函数并忘记更改文档字符串或打错字很容易。[是的,我知道文档字符串已经复制了函数体。请忽略这个;funny_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?
还有其他方法吗?