This recipe shows how to modify the class hierarchy of an instance object after it has already been instantiated.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | def adopt_class(klass, obj, *args, **kwds):
'reclass obj to inherit klass; call __init__ with *args, **kwds'
classname = '%s_%s' % (klass.__name__, obj.__class__.__name__)
obj.__class__ = new.classobj(classname, (klass, obj.__class__), {})
klass.__init__(obj, *args, **kwds)
def demo():
class Sandwich:
def __init__(self, ingredients):
self.ingredients = ingredients
def __repr__(self):
return reduce((lambda a,b: a+' and '+b), self.ingredients)
class WithSpam:
def __init__(self, spam_count):
self.spam_count = spam_count
def __repr__(self):
return Sandwich.__repr__(self) + self.spam_count * ' and spam'
pbs = Sandwich(['peanut butter', 'jelly'])
adopt_class(WithSpam, pbs, 2)
print pbs
|
Sometimes this is the cleanest way out of certain class hierarchy problems that arise when you wish to avoid module interdependencies (e.g. within a layered architecture). Also useful if you want to add functionality to objects that are created by third party modules when modifying those modules is undesirable.
In the following example, the programmer has these constraints:
There are several classes in objects.py, and more will be added in the future.
Objects.py must not import or know about graphics.py, since the latter is not available in all configurations. Therefore, class 'G' cannot be a base class for the objects.py classes.
Graphics.py should not require modification in order to support additional classes that may be added to objects.py.
#
objects.py
class A(Base): ...
class B(Base): ...
def factory(...): ... returns an instance of A or B or ...
#
graphics.py
from recipies import adopt_class
class G: ... provides graphical capabilities
def gfactory(...): obj = factory(...) adopt_class(G, obj, ...) return obj