Decorating python3.5 statements

I am trying to decorate all the methods in the class and I have succeeded in this code, but I am also trying to record calls to operators, for example * + - / , is there any way to decorate them or something like getattr(self,"*") register calls?


 class Logger(object): def __init__(self, bool): self.bool = bool def __call__(self, cls): class DecoratedClass(cls): def __init__(cls, *args, **kwargs): super().__init__(*args, **kwargs) if not(self.bool): return methods = [func for func in dir(cls) if callable(getattr(cls, func)) and not func.startswith("__class")] for func in methods: old_func = getattr(cls, func) def decorated_function(fname, fn): def loggedFunction(*args, **kwargs): print("Calling {0} from {3} with params {1} and kwargs {2}".format(fname.upper(), args, kwargs, cls)) return fn(*args, **kwargs) return loggedFunction setattr(cls, func, decorated_function(func, old_func)) return DecoratedClass 

 @Logger(True) class DummyClass(): def __init__(self,foo): self.foo = foo def bar(self): print(self.foo) def __mul__(self,other): print("Hello",other) if __name__ == '__main__': a = DummyClass('hola') a.method() a.__mul__(a) #this is logged print(a*a) #this is not logged by decorator 

+5
source share
2 answers

Thanks to Łukasz, the script works here.

The difficulty I came across is to process multiple instances and avoid reusing the same class methods. To deal with this problem, I track decorated class methods ( cls.__logged ).

Another difficulty is considering magic methods like __setattr__ , __getattribute__ , __repr__ , ... My solution is to ignore them, except for the list you have to define at startup ( loggable_magic_methods ).

 from functools import wraps loggable_magic_methods = ['__mul__',] def is_magic_method(method): return method.startswith('__') class Logger(object): def __init__(self, bool): self.bool = bool def __call__(self, cls): class LoggedClass(cls): cls.__logged = [] def __init__(instance, *args, **kwargs): super().__init__(*args, **kwargs) if not(self.bool): return methods = [funcname for funcname in dir(instance) if callable(getattr(instance, funcname)) and (funcname in loggable_magic_methods or not is_magic_method(funcname))] def logged(method): @wraps(method) def wrapper(*args, **kwargs): print (method.__name__, args, kwargs, cls) return method(*args, **kwargs) return wrapper for funcname in methods: if funcname in cls.__logged: continue if is_magic_method(funcname): setattr(cls, funcname, logged(getattr(cls, funcname))) cls.__logged.append(funcname) else: setattr(instance, funcname, logged(getattr(instance, funcname))) return LoggedClass @Logger(True) class DummyClass(): def __init__(self, foo, coef): self.foo = foo self.coef = coef def bar(self): print(self.foo) def __mul__(self, other): print(self.foo) print(other.foo) return self.coef * other.coef if __name__ == '__main__': a = DummyClass('hola', 1) a.bar() print() print(a.__mul__(a)) print() print(a*a) print() b = DummyClass('gracias', 2) b.bar() print() print(b.__mul__(a)) print() print(b*a) 
+2
source

You are currently correcting the values ​​in the instance. Your use of the cls in __init__ signature is a false friend - it is actually an old simple self in this case.

If you want to override magic methods, the interpreter searches for them in class objects, not in instances.

Minimal example:

 class DummyClass: def __init__(self, foo): self.foo = foo def __mul__(self, other): return self.foo * other.foo def logged(method): def wrapper(*args, **kwargs): print (method.__name__, args, kwargs) return method(*args, **kwargs) return wrapper DummyClass.__mul__ = logged(DummyClass.__mul__) a = DummyClass(1) b = DummyClass(2) assert a * a == 1 assert a * b == 2 assert b * b == 4 

Each call is logged.

 >>> a = DummyClass(1) >>> b = DummyClass(2) >>> assert a * a == 1 __mul__ (<__main__.DummyClass object at 0x00000000011BFEB8>, <__main__.DummyClass object at 0x00000000011BFEB8>) {} >>> assert a * b == 2 __mul__ (<__main__.DummyClass object at 0x00000000011BFEB8>, <__main__.DummyClass object at 0x00000000011BF080>) {} >>> assert b * b == 4 __mul__ (<__main__.DummyClass object at 0x00000000011BF080>, <__main__.DummyClass object at 0x00000000011BF080>) {} 

I will leave the task of rewriting approaches to fake monkeys.

+2
source

Source: https://habr.com/ru/post/1260190/