I've found myself in an unusual situation where I need to change the MRO of a class at runtime.
The code:
class A(object):
    def __init__(self):
        print self.__class__
        print "__init__ A"
        self.hello()
    def hello(self):
        print "A hello"
class B(A):
    def __init__(self):
        super(B, self).__init__()
        print "__init__ B"
        self.msg_str = "B"
        self.hello()
    def hello(self):
        print "%s hello" % self.msg_str
a = A()
b = B()
As to be expected, this fails as the __init__ method of A (when called from B) calls B's hello which attempts to access an attribute before it exists.
The issue is that I'm constrained in the changes I can make:
- B must subclass A
- A cannot be changed
- Both A and B require a hello method
- B cannot initialise other attributes before calling the super __init__
I did solve this conceptually, by changing the MRO at runtime. In brief, during B's __init__, but before calling super __init__, the MRO would be changed so that A would be searched for methods first, thereby calling A's hello instead of B's (and therefore failing).
The issue is that MRO is read only (at class runtime).
Is there another way to implement this ? Or possibly a different solution altogether (that still respects the aforementioned constraints) ?
 
                        
The other provided answers are advisable if you are not bound by the constraints mentioned in the question. Otherwise, we need to take a journey into mro hacks and metaclass land.
After some reading, I discovered you can change the mro of a class, using a metaclass.
This however, is at class creation time, not at object creation time. Slight modification is necessary.
The metaclass provides the
mromethod, which we overload, that is called during class creation (the metaclass'__new__call) to produce the__mro__attribute.The
__mro__attribute is not a normal attribute, in that:__new__callHowever, it appears to be recalculated (using the
mromethod) when a class' base is changed. This forms the basis of the hack.In brief:
B) is created using a metaclass (change_mro_meta). This metaclass provides:__mro__attributechange_mro) to control the mro behaviourAs mentioned, modifying the mro of a class while in its
__init__is not thread safe.The following may disturb some viewers. Viewer discretion is advised.
The hack:
Some notes:
The
hack_mro,fix_mroandrecalc_mromethods are staticmethods to the metaclass but classmethods to the class. It did this, instead of multiple inheritance, because I wanted to group the mro code together.The
mromethod itself returns the default ordinarily. Under the hack condition, it appends the second element of the default mro (the immediate parent class) to the mro, thereby causing the parent class to see its own methods first before the subclass'.I'm unsure of the portability of this hack. Its been tested on 64bit CPython 2.7.3 running on Windows 7 64bit.
Don't worry, I'm sure this won't end up in production code somewhere.