关于python:继承装饰类

Inheriting from decorated classes

我想用另一个班级装饰一个班级。我还想从装饰类继承,但我得到了一些错误。以下是我的代码:

1
2
3
4
5
6
7
8
9
10
class Decorator:
    def __init__(self, decorated):
        pass

@Decorator
class Foo:
    pass

class Goo(Foo):
    pass

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

Traceback (most recent call last):
   File"test.py", line 9, in
      class Goo(Foo):
TypeError: __init__() takes exactly 2 positional arguments (4 given)

通过向Decorator添加另一个init函数…

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

…我得到以下输出:


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

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


我将尝试回答"那些参数是什么"问题。此代码:

1
2
3
@Decorator
class Foo:
    pass

相当于:

1
2
3
class Foo:
    pass
Foo = Decorator(Foo)

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

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

这就是你如何在Decorator.__init__中得出这些论点的。

有关这方面的更多信息,请参见:http://www.python.org/download/releases/2.2.3/descrintro/元类(尤其是"执行类语句时……"部分)


在类被定义之后,您是否试图向类添加一个mixin?如果是这样,你可以用这种方式注入混合物:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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()

印刷品

1
2
3
mix
foo
goo

如果您不想使用inject_class的一般性,可以制作一个专门的类修饰器,它只在Decorator中混合:

1
2
3
4
5
6
7
8
9
10
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')

结果是一样的。


我也遇到了同样的问题,下面的解决方案对我有效:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
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__()方法的改变,它由以下几行组成:

1
2
3
4
5
        def new (cls):
            self._createInstance(cls)
            self._postInstanceCreation()
            return self
        self._logic.__new__ = new

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
>>> 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
>>>