Наследование от украшенных классов - PullRequest
3 голосов
/ 20 сентября 2011

Я пытаюсь украсить класс другим классом.Я также хочу наследовать от украшенного класса, но я получаю некоторые ошибки.Вот мой код:

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

@Decorator
class Foo:
    pass

class Goo(Foo):
    pass

Ошибка, которую я получаю при попытке подкласса из Foo, заключается в следующем:

Traceback (последний вызов был последним):
File"test.py", строка 9, в
классе Goo (Foo):
TypeError: __init __ () принимает ровно 2 позиционных аргумента (задано 4)

Путем добавления другой функции инициализациина Decorator ...

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

... Я получаю следующий вывод:


Goo
(<__ main __. Объект Decorator в 0x010073B0>,)
{'__module__': '__main __'}

Что это за параметры и как их использоватьвнутри Decorator?

Ответы [ 3 ]

8 голосов
/ 20 сентября 2011

Я постараюсь ответить на вопрос «что это за параметры».Этот код:

@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 (в частности, часть "Когда выполняется оператор класса ...")

2 голосов
/ 20 сентября 2011

Вы пытаетесь добавить 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')

результат тот же.

0 голосов
/ 01 октября 2015

У меня была такая же проблема, и у меня работает следующее решение:

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