How to dynamically change base class of instances at runtime? How to dynamically change base class of instances at runtime? python python

How to dynamically change base class of instances at runtime?


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


I've been struggling with this too, and was intrigued by your solution, but Python 3 takes it away from us:

AttributeError: attribute '__dict__' of 'type' objects is not writable

I actually have a legitimate need for a decorator that replaces the (single) superclass of the decorated class. It would require too lengthy a description to include here (I tried, but couldn't get it to a reasonably length and limited complexity -- it came up in the context of the use by many Python applications of an Python-based enterprise server where different applications needed slightly different variations of some of the code.)

The discussion on this page and others like it provided hints that the problem of assigning to __bases__ only occurs for classes with no superclass defined (i.e., whose only superclass is object). I was able to solve this problem (for both Python 2.7 and 3.2) by defining the classes whose superclass I needed to replace as being subclasses of a trivial class:

## T is used so that the other classes are not direct subclasses of object,## since classes whose base is object don't allow assignment to their __bases__ attribute.class T: passclass A(T):    def __init__(self):        print('Creating instance of {}'.format(self.__class__.__name__))## ordinary inheritanceclass B(A): pass## dynamically specified inheritanceclass C(T): passA()                 # -> Creating instance of AB()                 # -> Creating instance of BC.__bases__ = (A,)C()                 # -> Creating instance of C## attempt at dynamically specified inheritance starting with a direct subclass## of object doesn't workclass D: passD.__bases__ = (A,)D()## Result is:##     TypeError: __bases__ assignment: 'A' deallocator differs from 'object'


I can not vouch for the consequences, but that this code does what you want at py2.7.2.

class Friendly(object):    def hello(self):        print 'Hello'class Person(object): pass# we can't change the original classes, so we replace themclass newFriendly: passnewFriendly.__dict__ = dict(Friendly.__dict__)Friendly = newFriendlyclass newPerson: passnewPerson.__dict__ = dict(Person.__dict__)Person = newPersonp = Person()Person.__bases__ = (Friendly,)p.hello()  # prints "Hello"

We know that this is possible. Cool. But we'll never use it!