How to dynamically change base class of instances at runtime?
dynamic, inheritance, python
Solution
Ok, again, this is not something you should normally do, this is for informational purposes only.
Where Python looks for a method on an instance object is determined by the `__mro__` attribute of the class which defines that object (the M ethod R esolution O rder attribute). Thus, if we could modify the `__mro__` of `Person`, we'd get the desired behaviour. Something like:
setattr(Person, '__mro__', (Person, Friendly, object))
The problem is that `__mro__` is a readonly attribute, and thus setattr won't work. Maybe if you're a Python guru there's a way around that, but clearly I fall short of guru status as I cannot think of one.
A possible workaround is to simply redefine the class:
def modify_Person_to_be_friendly():
# so that we're modifying the global identifier 'Person'
global Person
# now just redefine the class using type(), specifying that the new
# class should inherit from Friendly and have all attributes from
# our old Person class
Person = type('Person', (Friendly,), dict(Person.__dict__))
def main():
modify_Person_to_be_friendly()
p = Person()
p.hello() # works!
What this doesn't do is modify any previously created `Person` instances to have the `hello()` method. For example (just modifying `main()`):
def main():
oldperson = Person()
ModifyPersonToBeFriendly()
p = Person()
p.hello()
# works! But:
oldperson.hello()
# does not
If the details of the `type` call aren't clear, then read e-satis' excellent answer on 'What is a metaclass in Python?'.
Problem
This article has a snippet showing usage of `__bases__` to dynamically change the inheritance hierarchy of some Python code, by adding a class to an existing classes collection of classes from which it inherits. Ok, that's hard to read, code is probably clearer: ``` class Friendly: def hello(self): print 'Hello' class Person: pass p = Person() Person.__bases__ = (Friendly,) p.hello() # prints "Hello" ``` That is, `Person` doesn't inherit from `Friendly` at the source level, but rather this inheritance relation is added dynamically at runtime by modification of the `__bases__`attribute of the Person class. However, if you change `Friendly` and `Person` to be new style classes (by inheriting from object), you get the following error: ``` TypeError: __bases__ assignment: 'Friendly' deallocator differs from 'object' ``` A bit of Googling on this seems to indicate some incompatibilities between new-style and old style classes in regards to changing the inheritance hierarchy at runtime. Specifically: "New-style class objects don't support assignment to their bases attribute". My question, is it possible to make the above Friendly/Person example work using new-style classes in Python 2.7+, possibly by use of the `__mro__` attribute? Disclaimer: I fully realise that this is obscure code. I fully realize that in real production code tricks like this tend to border on unreadable, this is purely a thought experiment, and for funzies to learn something about how Python deals with issues related to multiple inheritance.