definner(self, *args, **kwargs): ret = method(self, *args, **kwargs) logger.info(f'Call method {method.__name__} of {self} with {args, kwargs} returns {ret}') return ret
return inner
classA: a:int def__init__(self, a): self.a = a
@method_logger defaddX(self, x: int): returnself.a + x
def__repr__(self): returnf'A(a={self.a})'
a = A(1) a.addX(2) a.addX(x=3)
1 2
INFO:__main__:Call method addX of A(a=1) with ((2,), {}) returns 3 INFO:__main__:Call method addX of A(a=1) with ((), {'x': 3}) returns 4
defmethod_logger_x(method, obj): definner(*args, **kwargs): ret = method(*args, **kwargs) logger.info(f'Call method {method.__name__} of {obj} with {args, kwargs} returns {ret}') return ret return inner
classMethodLogger: def__getattribute__(self, key): value = super().__getattribute__(key) ifcallable(value) andnot key.startswith('__'): return method_logger_x(value, self) return value
@dataclass classAccount(MethodLogger): uid: str banlance: int frozen: bool = False
deftranserTo(self, target: 'Account', value:int): self.banlance -= value target.banlance += value returnself, target
deffreeze(self, reason:str): self.frozen = True
a = Account('aaaa', 10) b = Account('bbbb', 10) a.transerTo(b, 5) a.freeze('Dangerous Action')
1 2
INFO:__main__:Call method transerTo of Account(uid='aaaa', banlance=5, frozen=False) with ((Account(uid='bbbb', banlance=15, frozen=False), 5), {}) returns (Account(uid='aaaa', banlance=5, frozen=False), Account(uid='bbbb', banlance=15, frozen=False)) INFO:__main__:Call method freeze of Account(uid='aaaa', banlance=5, frozen=True) with (('Dangerous Action',), {}) returns None
值得注意的有两点:
我们在实现自定义的__getattribute__方法的时候判断访问的对象的属性是方法而不是其他字段的时候做了两个判断,一个是当前属性是否可执行(callable(value) is True),另一个是当前的属性名称不能以双下划线__开头,不然的话对象实例化调用__init__方法,打印日志的时候调用__str__或者__repr__方法的时候也会记录日志。