views:

87

answers:

2

The following code doesn't work in Python 3.x, but it used to work with old-style classes:

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

class Base:
    pass

Base.__bases__ += (Extender,)

Base().extension()

Question is simple: How can I add dynamically (at runtime) a super class to a class in Python 3.x?

But I'm ready the answer will be hard! )

+2  A: 

As for me it is impossible. But you can create new class dynamically:

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

class Base(object):
    pass

Base = type('Base', (Base, Extender, object), {})
Base().extension()
Mykola Kharechko
Thank you! Your solution works well! :)But I've updated it a little :$ It's enough to add a new base class only: `Base = type('Base', (Extender,), {})` If you add 'Base' and 'object' classes again they will appear twice in the MRO and the interpreter will look for attributes slowly, i think.
DenisKolodin
@DenisKolodin: The two Bases in the MRO are different classes: the new one and then the old one. Look at `[id(cls) for cls in Base.__mro__]` to see they are different.
unutbu
I've tested it fully and have found one serious difference (((Type will changed for new instances only, but with __bases__ I can change attributes in exists instances that I need.This solution isn't similar! I still search (
DenisKolodin
@DenisKolodin: maybe try to use copy.deepcopy
Mykola Kharechko
A: 

It appears that it is possible to dynamically change Base.__bases__ if Base.__base__ is not object. (By dynamically change, I mean in such a way that all pre-existing instances that inherit from Base also get dynamically changed. Otherwise see Mykola Kharechko's solution).

If Base.__base__ is some dummy class TopBase, then assignment to Base.__bases__ seems to work:

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

This was tested to work in Python 2 (for new- and old-style classes) and for Python 3. I have no idea why it works while this does not:

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

class Base:
    pass

Base.__bases__ += (Extender,)
# TypeError: Cannot create a consistent method resolution
# order (MRO) for bases Extender, object
unutbu