python - 应用“setattr”后如何捕获原始函数的参数?
问题描述
我在一个项目中有一个模块,该模块先于其他任何内容加载,并且该模块需要将自定义逻辑添加到我从数据库中检索的函数列表中。要添加这个自定义逻辑,我使用内置的 python 函数 setattr。
此外,同一个项目的人希望为同一列表中的某些函数捕获参数。为了捕获参数,他们使用了 inspect.getcallargs 函数。但是,他们获取包装函数的参数,而不是获取原始函数的参数。
我理解为什么会发生这种情况,但没有解决问题的方法。
我的装饰器/包装器如下所示:
def func_decorator(func, some_other_params):
def func_to_return(*args, **kwargs):
# some code
payload = func(*args, **kwargs)
# some other code
return payload
return func_to_return
因此,在遍历要装饰的函数列表的函数中,我有如下所示的内容:
def add_custom_logic_to_funcs:
....
for func_name in funcs:
func = getattr(func_class, func_name)
setattr(func_class, func_name, func_decorator(func, some_params))
如果假设原始函数如下所示:
class A:
def(self, x=None, y=None, z=1):
#some code
调用 inspect.getcallargs 会返回 {'args': (some object at 0x000000001BCA8C50,), 'kwargs': {}} 而不是 {'self': some object at 0x000000001BCA8C50, 'x': None, 'y': None ,'z':1}
解决方案
您将需要两件事 - 首先是您的装饰器本身是用 装饰的functools.wraps()
,因此装饰函数上的一些元数据被添加到装饰函数中:
from functools import decorator
def func_decorator(func, some_other_params):
@wraps(func)
def func_to_return(*args, **kwargs):
# some code
payload = func(*args, **kwargs)
# some other code
return payload
return func_to_return
但是,不要inspect.getcallargs
反映装饰的签名。取而代之的是,您必须使用inspect.signature
- 它返回一个更丰富的Signature
对象,但它具有一个.parameters
看起来像 return from 的属性.getcallargs
:
In [26]: def deco(func):
...: @wraps(func)
...: def wrapper(*args, **kw):
...: return func(*args, **kw)
...: return wrapper
...:
In [27]: class A:
...: @deco
...: def b(self, a, b=2):
...: pass
...:
In [28]: inspect.signature(A().b).parameters
Out[28]: mappingproxy({'a': <Parameter "a">, 'b': <Parameter "b=2">})
In [29]: inspect.signature(A.b).parameters
Out[29]:
mappingproxy({'self': <Parameter "self">,
'a': <Parameter "a">,
'b': <Parameter "b=2">})