首页 > 解决方案 > 应用“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}

标签: pythonpython-2.xbuilt-in

解决方案


您将需要两件事 - 首先是您的装饰器本身是用 装饰的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">})


推荐阅读