【问题标题】:Decorate operators python3.5装饰算子python3.5
【发布时间】:2017-04-07 01:25:06
【问题描述】:

我正在尝试装饰类中的所有方法,并且我成功使用了此代码,但我也尝试记录对 * + - / 等运算符的调用,有没有办法装饰它们或类似 getattr(self,"*") 的东西记录通话?


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

【问题讨论】:

    标签: python python-3.x python-decorators


    【解决方案1】:

    感谢 Ł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>) {}
      

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-01-31
        • 2013-05-23
        • 2015-03-21
        • 2018-06-30
        • 2017-04-22
        • 2015-07-14
        • 1970-01-01
        相关资源
        最近更新 更多