【问题标题】:Inheriting from decorated classes从装饰类继承
【发布时间】:2011-11-20 14:18:14
【问题描述】:

我正在尝试用另一个班级装饰一个班级。我也想从装饰类继承,但我得到了一些错误。这是我的代码:

class Decorator:
    def __init__(self, decorated):
        pass

@Decorator
class Foo:
    pass

class Goo(Foo):
    pass

当我尝试从Foo 子类化时得到的错误是:

回溯(最近一次通话最后一次):
文件“test.py”,第 9 行,在
类咕(Foo):
TypeError: __init__() 正好采用 2 个位置参数(给定 4 个)

通过向Decorator添加另一个初始化函数...

def __init__(self, *args):
    for arg in args:
        print(arg)

...我得到以下输出:



(<__main__.decorator object at>,)
{'__module__': '__main__'}

这些参数是什么?我应该如何在Decorator 中使用它们?

【问题讨论】:

    标签: python inheritance decorator


    【解决方案1】:

    我将尝试回答“这些参数是什么”的问题。这段代码:

    @Decorator
    class Foo:
        pass
    

    相当于:

    class Foo:
        pass
    Foo = Decorator(Foo)
    

    这意味着Foo 最终成为Decorator 类的实例,而不是一个类。

    当您尝试将此实例用作类的基类 (Goo) 时,Python 必须确定一个元类,用于创建新类。在这种情况下,它将使用等于DecoratorFoo.__class__。然后它将使用(name, bases, dict) 参数调用元类并期望它返回一个新类。

    这就是你在Decorator.__init__ 中最终得到这些参数的方式。

    可以在此处找到有关此内容的更多信息: http://www.python.org/download/releases/2.2.3/descrintro/#metaclasses (特别是“当执行类语句时......”部分)

    【讨论】:

      【解决方案2】:

      您是否尝试在定义类之后向该类添加 MixIn? 如果是这样,您可以通过这种方式注入 MixIn:

      def inject_class(mixin):
          def _inject_class(cls):
              return type(cls.__name__,(mixin,)+cls.__bases__,dict(cls.__dict__))
          return _inject_class
      
      class MixIn(object):
          def mix(self):
              print('mix')
      
      @inject_class(MixIn)
      class Foo(object):
          def foo(self):
              print('foo')
      
      class Goo(Foo):
          def goo(self):
              print('goo')
      
      goo=Goo()
      goo.mix()
      goo.foo()
      goo.goo()
      

      打印

      mix
      foo
      goo
      

      如果你不想要inject_class 的通用性,你可以制作一个专门的类装饰器,它只混入Decorator

      def decorate(cls):
          class Decorator(object):
              def deco(self):
                  print('deco')
          return type(cls.__name__,(Decorator,)+cls.__bases__,dict(cls.__dict__))
      
      @decorate
      class Foo(object):
          def foo(self):
          print('foo')
      

      结果是一样的。

      【讨论】:

        【解决方案3】:

        我遇到了同样的问题,以下解决方案对我有用:

        from functools import update_wrapper
        class decoratorBase():
            def __new__(cls, logic):
                self = object.__new__(cls)
                self.__init__(logic)
                def new (cls):
                    #cls is the decorated class type, not the decorator class type itself
                    self._createInstance(cls)
                    self._postInstanceCreation()
                    return self
                self._logic.__new__ = new
                #return the wrapped class and not a wrapper
                return self._logic
            def __init__(self, logic):
                #logic is the decorated class
                self._logic = logic
            def _createInstance(self, cls):
                self._logicInstance = object.__new__(cls)
                self._logicInstance.__init__()
            def _postInstanceCreation(self):
                pass
        
        class factory(decoratorBase):
            def __init__(self, *largs, **kwargs):
                super().__init__(*largs, **kwargs)
                self.__instance = None
            def _createInstance(self, cls):
                self._logicInstance = None
                self._cls = cls
            def _postInstanceCreation(self):
                update_wrapper(self, self._cls)
            def __call__(self, userData, *largs, **kwargs):
                logicInstance = object.__new__(self._cls)
                logicInstance.__init__(*largs, **kwargs)
                logicInstance._update(userData)
                return logicInstance
        
        class singelton(decoratorBase):
            def _postInstanceCreation(self):
                update_wrapper(self, self._logicInstance)
            def __call__(self, userData):
                self._logicInstance._update(userData)
                return self._logicInstance
        
        class base():
            def __init__(self):
                self.var = 0
                print ("Create new object")
            def __call__(self):
                self.var += self._updateValue()
            def _update(self, userData):
                print ("Update object static value with {0}".format(userData))
                self.var = userData
        
        @factory
        class factoryTestBase(base):
        
            def __call__(self):
                super().__call__()
                print("I'm a factory, here is the proof: {0}".format(self.var))
            def _updateValue(self):
                return 1
        
        class factoryTestDerived(factoryTestBase):
            def _updateValue(self):
                return 5
        
        @singelton
        class singeltonTestBase(base):
            def __call__(self):
                super().__call__()
                print("I'm a singelton, here is the proof: {0}".format(self.var))
            def _updateValue(self):
                return 1
        
        class singeltonTestDerived(singeltonTestBase):
            def _updateValue(self):
                return 5
        

        这种方法的神奇之处在于 __new__() 方法的重载,对于装饰器本身以及装饰器返回的“包装器”也是如此。我将单词包装器放在引号中,因为实际上没有包装器。相反,装饰类由装饰器交替并返回。使用此方案,您可以从装饰类继承。最重要的是修饰类的__new__()方法的变化,由以下几行组成:

                def new (cls):
                    self._createInstance(cls)
                    self._postInstanceCreation()
                    return self
                self._logic.__new__ = new
        

        使用它,您可以在从装饰类创建对象期间访问装饰器方法,例如self._createInstance()。您甚至有机会从您的装饰器继承(如示例中所示)。

        现在让我们运行一个简单的例子:

        >>> factoryObjCreater = factoryTestBase()
        >>> factoryObj1 = factoryObjCreater(userData = 1)
        Create new object
        Update object static value with 1
        >>> factoryObj2 = factoryObjCreater(userData = 1)
        Create new object
        Update object static value with 1
        >>> factoryObj1()
        I'm a factory, here is the proof: 2
        >>> factoryObj2()
        I'm a factory, here is the proof: 2
        >>> factoryObjDerivedCreater = factoryTestDerived()
        >>> factoryObjDerived1 = factoryObjDerivedCreater(userData = 2)
        Create new object
        Update object static value with 2
        >>> factoryObjDerived2 = factoryObjDerivedCreater(userData = 2)
        Create new object
        Update object static value with 2
        >>> factoryObjDerived1()
        I'm a factory, here is the proof: 7
        >>> factoryObjDerived2()
        I'm a factory, here is the proof: 7
        >>> singeltonObjCreater = singeltonTestBase()
        Create new object
        >>> singeltonObj1 = singeltonObjCreater(userData = 1)
        Update object static value with 1
        >>> singeltonObj2 = singeltonObjCreater(userData = 1)
        Update object static value with 1
        >>> singeltonObj1()
        I'm a singelton, here is the proof: 2
        >>> singeltonObj2()
        I'm a singelton, here is the proof: 3
        >>> singeltonObjDerivedCreater = singeltonTestDerived()
        Create new object
        >>> singeltonObjDerived1 = singeltonObjDerivedCreater(userData = 2)
        Update object static value with 2
        >>> singeltonObjDerived2 = singeltonObjDerivedCreater(userData = 2)
        Update object static value with 2
        >>> singeltonObjDerived1()
        I'm a singelton, here is the proof: 7
        >>> singeltonObjDerived2()
        I'm a singelton, here is the proof: 12
        >>>  
        

        【讨论】:

          猜你喜欢
          • 2012-11-14
          • 2011-03-23
          • 2018-06-27
          • 2021-02-21
          • 2014-03-07
          • 2018-04-17
          • 2011-09-18
          • 2023-04-09
          • 2016-12-05
          相关资源
          最近更新 更多