从修饰类中继承

4 投票
3 回答
3666 浏览
提问于 2025-04-17 02:38

我正在尝试用一个类来装饰另一个类。同时,我还想从被装饰的类继承,但遇到了一些错误。下面是我的代码:

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

@Decorator
class Foo:
    pass

class Goo(Foo):
    pass

当我尝试从 Foo 这个类继承时,出现的错误是:

错误追踪(最近的调用在最前面):
   文件 "test.py",第 9 行,
      类 Goo(Foo):
类型错误:__init__() 需要 2 个位置参数(给了 4 个)

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

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

... 我得到了以下输出:

<class '__main__.Foo'>
Goo
(<__main__.Decorator object at 0x010073B0>,)
{'__module__': '__main__'}

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

3 个回答

0

我之前也遇到过同样的问题,下面这个解决办法对我有效:

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
>>>  
3

你是不是想在一个类定义之后再给它添加一个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')

结果是一样的。

8

我来试着解释一下“那些参数是什么”这个问题。这段代码:

@Decorator
class Foo:
    pass

其实是等同于:

class Foo:
    pass
Foo = Decorator(Foo)

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

当你尝试把这个实例用作另一个类(Goo)的基础时,Python 需要确定一个元类来创建这个新类。在这种情况下,它会使用 Foo.__class__,也就是 Decorator。然后它会用 (name, bases, dict) 这些参数来调用这个元类,并期待它返回一个新类。

这就是你在 Decorator.__init__ 中看到这些参数的原因。

关于这个内容,你可以在这里找到更多信息: http://www.python.org/download/releases/2.2.3/descrintro/#metaclasses (特别是“当一个类语句被执行时……”那部分)

撰写回答