2016-11-23 4 views
5

Ich versuche, alle Methoden in der Klasse zu dekorieren und ich mit diesem Code erfolgreich, aber ich versuche auch Aufrufe an Operatoren wie * + - / zu protokollieren, gibt es eine Möglichkeit, sie zu schmücken oder so etwas wie getattr(self,"*") um die Anrufe zu protokollieren?Dekorieren Operatoren 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 

Antwort

2

Dank Łukasz, hier ist ein funktionierendes Skript.

Eine Schwierigkeit, der ich begegnete, ist, mehrere Instanzen zu behandeln und zu vermeiden, mehrmals die gleichen Klassenmethoden zu dekorieren. Um dieses Problem zu behandeln, behalte ich die eingerichteten Klassenmethoden (cls.__logged) im Auge.

Eine weitere Schwierigkeit besteht darin, mit den magischen Methoden wie __setattr__, __getattribute__, __repr__ ... Meine Lösung zu behandeln ist, sie zu ignorieren, mit Ausnahme einer Liste, die Sie beim Start definieren müssen (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

Sie patchen Werte auf Instanz Aktuell. Ihre Verwendung von cls in __init__ Unterschrift ist falscher Freund - eigentlich ist es in diesem Fall alte Ebene self.

Wenn Sie magische Methoden überschreiben möchten, sucht der Interpreter nach Klassenobjekten, nicht nach Instanzen.

Minimal Beispiel:

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 

Jeder Anruf wird protokolliert.

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

Ich überlasse Ihnen die Aufgabe, den Affe-Patching-Ansatz neu zu schreiben.