2010-07-07 2 views
7

Следующий код не работает в Python 3.x, но он используется для работы с классами старого стиля:__bases__ не работает! Что дальше?

class Extender: 
    def extension(self): 
     print("Some work...") 

class Base: 
    pass 

Base.__bases__ += (Extender,) 

Base().extension() 

Вопрос прост:Как я могу добавить динамически (во время выполнения) а супер класс для класса в Python 3.x?

Но я готов, ответ будет сложным!)

+9

Ваши базы больше не принадлежат вам? И ответ будет тяжелым? – Borealid

+0

Я думаю, что это уже невозможно, так как все классы являются классами нового стиля в Python 3, а MRO будет неоднозначным, если в «Base» были как «object», так и «Extender» в качестве базовых классов. Создайте новый класс, который наследуется от 'Base' и' Extender' вместо этого. «Расширитель» также может быть ABC. – Philipp

ответ

4

Что касается меня невозможно. Но вы можете создать новый класс динамически:

class Extender(object): 
    def extension(self): 
     print("Some work...") 

class Base(object): 
    pass 

Base = type('Base', (Base, Extender, object), {}) 
Base().extension() 
+0

Спасибо! Ваше решение работает хорошо! :) Но я немного его обновил: $ Достаточно добавить только базовый класс: 'Base = type ('Base', (Extender,), {})' Если вы добавите 'Base' и 'object 'классы снова они появятся дважды в MRO, и интерпретатор будет искать атрибуты медленно, я думаю. – DenisKolodin

+0

@DenisKolodin: Две базы в MRO - это разные классы: новый, а затем старый. Посмотрите на '[id (cls) для cls в Base .__ mro __]', чтобы увидеть, что они разные. – unutbu

+0

Я протестировал его полностью и обнаружил одно серьезное различие ((( Тип будет изменен только для новых экземпляров, но с __bases__ Я могу изменить атрибуты в существующих экземплярах, которые мне нужны. Это решение не похоже! ( – DenisKolodin

3

Оказывается, что можно динамически изменять Base.__bases__ если Base.__base__ не object. (Динамически меняя, я имею в виду таким образом, что все ранее существовавшие экземпляры, которые наследуют от Base, также динамически меняются. В противном случае см. Mykola Kharechko's solution).

Base.__base__ Если некоторый фиктивный класс TopBase, то назначение Base.__bases__, кажется, работает:

class Extender(object): 
    def extension(self): 
     print("Some work...") 

class TopBase(object): 
    pass 

class Base(TopBase): 
    pass 

b=Base() 
print(Base.__bases__) 
# (<class '__main__.TopBase'>,) 

Base.__bases__ += (Extender,) 
print(Base.__bases__) 
# (<class '__main__.TopBase'>, <class '__main__.Extender'>) 
Base().extension() 
# Some work... 
b.extension() 
# Some work... 

Base.__bases__ = (Extender, TopBase) 
print(Base.__bases__) 
# (<class '__main__.Extender'>, <class '__main__.TopBase'>) 
Base().extension() 
# Some work... 
b.extension() 
# Some work... 

Это был протестирован для работы в Python 2 (для классов новый- и старого стиля) и Python 3. Я не знаю, почему это работает, пока это не так:

class Extender(object): 
    def extension(self): 
     print("Some work...") 

class Base(object): 
    pass 

Base.__bases__ = (Extender, object) 
# TypeError: __bases__ assignment: 'Extender' deallocator differs from 'object' 
+0

Причина ошибки «невозможно создать согласованное ..» заключается в том, что вы пытаетесь изменить Base .__ base__ из значения (объекта) в (объект, расширитель) , что является неправильным способом в соответствии с mro-order. (Аналогично, вы не можете сказать «класс X (объект, расширитель)», какие ошибки схожим образом. Однако «Base .__ bases__ + = (Extender,) + Base .__ bases__ 'будет просто бросать' 'E' deallocator отличается от 'object'', что является еще одной ошибкой, но все еще является ошибкой. – driax

+0

@driax: Да, спасибо за объяснение. – unutbu

+2

Для будущая ссылка, есть открытая проблема CPython, связанная с ошибкой deallocator: https://bugs.python.org/issue672115 (открыт с 2003 года, поэтому не задерживайте дыхание) – driax

Смежные вопросы