2016-11-23 34 views
5

我想装饰所有方法在类中,我成功与此代码,但我也试图记录调用运营商像* + - /,有没有什么方法来装饰它们或类似getattr(self,"*")记录通话?装饰运营商python3.5


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 

回答

2

感谢Łukasz,这是一个工作脚本。

我遇到的困难是处理多个实例,并避免多次装饰相同的类方法。为了解决这个问题,我跟踪了装饰类的方法(cls.__logged)。

另一个困难是对付魔术方法,如__setattr____getattribute____repr__,...我的解决办法是不理会他们,除了你必须在启动(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

目前正在配值的实例。您在__init__签名中使用cls的签名是虚假的朋友 - 实际上在这种情况下其实是老的self

如果您想重写魔术方法,解释器会在类对象上寻找它们,但不会在实例上寻找它们

最少例如:

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 

每个呼叫被记录。

>>> 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>) {} 

我会留下一个重写猴子修补方法给你的任务。