代码之家  ›  专栏  ›  技术社区  ›  pietroppeter

在可访问的python对象中记录指定函数参数值的函数装饰器

  •  0
  • pietroppeter  · 技术社区  · 6 年前

    class Borg:
        _shared_state = {}
    
        def __init__(self):
            self.__dict__ = self._shared_state
    
    class Log(Borg):
    
        def __init__(self):
            Borg.__init__(self)
            if not hasattr(self, 'tape'):
                self.tape = []
    
        def add(self, this):
            self.tape.append(this)
    
        def __str__(self):
            return '\n'.join([str(line) for line in self.tape])
    

    然后我有一个泛型调用对象和装饰器实现(缺少代码):

    import inspect
    import functools
    
    class Call:
    
        def __init__(self, name, **saved_arguments):
            self.name = name
            self.saved_arguments = saved_arguments
    
        def __str__(self):
            return f'Call(name={self.name}, saved_arguments={self.saved_arguments})'
    
    def record(func, save_args_names=None):
        if save_args_names is None:
            save_args_names = {}
        name = func.__name__
        args = inspect.getfullargspec(func).args
        if save_args_names and not set(save_args_names).issubset(set(args)):
            raise ValueError(f'Arguments not present in function: {set(save_args_names) - set(args)}')
        log = Log()
    
        @functools.wraps(func)
        def wrapper(*func_args, **func_kwargs):
            # **here** I am missing something to replace 0 with the correct values!
            saved_arguments = {a: 0 for a in save_args_names}
            log.add(Call(name, **saved_arguments))
            return_value = func(*func_args, **func_kwargs)
            return return_value
    
        return wrapper
    

    为了测试这一点,我设置了以下函数:

    def inner(x, add=0):
        return sum(x) + add
    
    def outer(number, add=0):
        x = range(number)
        return inner(x, add)
    

    基本的用例(不保存参数)是有效的:

    inner = record(inner)
    print(outer(1), outer(2), outer(3))
    print(Log())
    

    0 1 3
    Call(name=inner, saved_arguments={})
    Call(name=inner, saved_arguments={})
    Call(name=inner, saved_arguments={})
    

    我错过了什么

    inner = record(inner, save_args_names=['x'])
    print(outer(1), outer(2), outer(3))
    print(Log())
    

    要输出:

    0 1 3
    Call(name=inner, saved_arguments={'x': range(0, 1)})
    Call(name=inner, saved_arguments={'x': range(0, 2)})
    Call(name=inner, saved_arguments={'x': range(0, 3)})
    

    inner = record(inner, save_args_names=['x', 'add'])
    print(outer(1, 2), outer(2, 3), outer(3, 4))
    print(Log())
    

    应输出:

    2 4 7
    Call(name=inner, saved_arguments={'x': range(0, 1), 'add': 2})
    Call(name=inner, saved_arguments={'x': range(0, 2), 'add': 3})
    Call(name=inner, saved_arguments={'x': range(0, 3), 'add': 4})
    

    我觉得我很亲近 inspect 图书馆应该帮助我关闭这个,但一点帮助将不胜感激!

    1 回复  |  直到 6 年前
        1
  •  2
  •   Aran-Fey Kevin    6 年前

    你要找的功能是 Signature.bind . 定义您的 wrapper 功能如下:

    @functools.wraps(func)
    def wrapper(*func_args, **func_kwargs):
        signature = inspect.signature(func)
        bound_args = signature.bind(*func_args, **func_kwargs)
        saved_arguments = {a: bound_args.arguments[a] for a in save_args_names}
    
        log.add(Call(name, **saved_arguments))
        return_value = func(*func_args, **func_kwargs)
        return return_value