How to Dynamically Change Base Class of Instances at Runtime

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

Python: How to rebase or dynamically replace a class with a different base class

Make a factory function which dynamically generates the correct base class. In real life, you'd probably memoize the factory so that it always returns the same class object for a given parent, rather than creating multiple identical classes.

from PyQt4 import QtGui, QtCore

def BaseFactory(parent):

class UiMainBase(parent):
....

return UiMainBase

if __name__ == "__main__":
import sys
from PySide import QtGui as PySideGui

# UiMainBase = BaseFactory(QtGui.QMainWindow)
UiMainBase = BaseFactory(PySideGui.QMainWindow)
class MyGui(UiMainBase):
...

Ui = r"C:\pythonTest\ui\test.ui"
app = PySideGui.QApplication(sys.argv)
win = MyGui(Ui)

Dynamic inheritance in Python

Simply store the class-object in a variable (in the example below, it is named base), and use the variable in the base-class-spec of your class statement.

def get_my_code(base):

class MyCode(base):
def initialize(self):
...

return MyCode

my_code = get_my_code(ParentA)

Dynamically mixin a base class to an instance in Python

This dynamically defines a new class GentlePerson, and reassigns p's class to it:

class Gentleman(object):
def introduce_self(self):
return "Hello, my name is %s" % self.name

class Person(object):
def __init__(self, name):
self.name = name

p = Person("John")
p.__class__ = type('GentlePerson',(Person,Gentleman),{})
print(p.introduce_self())
# "Hello, my name is John"

Per your request, this modifies p's bases, but does not alter p's original class Person. Thus, other instances of Person are unaffected (and would raise an AttributeError if introduce_self were called).


Although it was not directly asked in the question, I'll add for googlers and curiosity seekers, that it is also possible to dynamically change a class's bases but (AFAIK) only if the class does not inherit directly from object:

class Gentleman(object):
def introduce_self(self):
return "Hello, my name is %s" % self.name

class Base(object):pass
class Person(Base):
def __init__(self, name):
self.name = name

p = Person("John")
Person.__bases__=(Gentleman,object,)
print(p.introduce_self())
# "Hello, my name is John"

q = Person("Pete")
print(q.introduce_self())
# Hello, my name is Pete

Python dynamic inheritance: How to choose base class upon instance creation?

What about defining the ImageZIP class on function-level ?

This will enable your dynamic inheritance.

def image_factory(path):
# ...

if format == ".gz":
image = unpack_gz(path)
format = os.path.splitext(image)[1][1:]
if format == "jpg":
return MakeImageZip(ImageJPG, image)
elif format == "png":
return MakeImageZip(ImagePNG, image)
else: raise Exception('The format "' + format + '" is not supported.')

def MakeImageZIP(base, path):
'''`base` either ImageJPG or ImagePNG.'''

class ImageZIP(base):

# ...

return ImageZIP(path)

Edit: Without need to change image_factory

def ImageZIP(path):

path = unpack_gz(path)
format = os.path.splitext(image)[1][1:]

if format == "jpg": base = ImageJPG
elif format == "png": base = ImagePNG
else: raise_unsupported_format_error()

class ImageZIP(base): # would it be better to use ImageZip_.__name__ = "ImageZIP" ?
# ...

return ImageZIP(path)


Related Topics



Leave a reply



Submit