This is a recipe for defining class hierarchies outside of class definitions. This way you could at, runtime, decide what class should act as the parent of a given class. A class could sometimes have one parent, sometimes another.
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | from new import classobj
def connectClasses( newClassName, parentClass, childClass ):
# Create an empty shell class with the passed in parentClass
# as the parent.
DupedChildClass = classobj( newClassName, (parentClass,), {} )
# Inject the parent class in to the child as a member, in case
# the child wants to call the parent.
DupedChildClass._parentClass = parentClass
# Copy references to all the members of the child class passed in
# to the new version of the child class connected to the
# given parent class. i.e. fill in the dupedChildClass shell created
# on previous line.
for k,v in childClass.__dict__.items():
# U can't overwrite the doc string for some reason
if k != '__doc__':
setattr( DupedChildClass, k, v )
return DupedChildClass
class Person:
def sayHello( self ):
print "Hi, I am a %s." % self._getMyGender()
class Man( Person ):
def _getMyGender( self ):
return "man"
class Woman( Person ):
def _getMyGender( self ):
return "woman"
class Insecure:
def _getMyGender( self ):
return self._parentClass._getMyGender( self ) + ', if that is OK'
# Create the two new classes
InsecureMan = connectClasses( 'InsecureMan', Man, Insecure )
InsecureWoman = connectClasses( 'InsecureWoman', Woman, Insecure )
# Instantiate classes
iMan = InsecureMan()
iWoman = InsecureWoman()
man = Man()
woman = Woman()
# Test the classes
man.sayHello()
woman.sayHello()
iMan.sayHello()
iWoman.sayHello()
# Expected output
'''
Hi, I am a man.
Hi, I am a woman.
Hi, I am a man, if that is OK.
Hi, I am a woman, if that is OK.
'''
|
Motivation:
I had an existing class hierarchy, where I sometimes wanted to make the same modification to two different classes, while leaving the original classes untouched. I considered the decorator pattern because it let me add functionality to objects of different types by intercepting then proxying method calls. My client using the decorated object saw these objects behaving polymorphicly because the method interface was constant, while object behavior varies by object. However, the decorator did not give me true polymorphism because the decorator class is not in the inheritence tree of the object it decorates. Therefore if code inside the decorated object calls one of it’s own member functions it will never execute the matching method of the decorator. What if you wanted true inheritance, but also wanted to add the same behavior to multiple types of objects? Lets take an example…
Lets say we have a Person class. From that class we derive both a Man and a Woman class.
PERSON
|
------------------------------
| |
MAN WOMAN
What if I introduce the concept of an insecure person? Lets say that insecurity manifests itself the same way in both sexes. I want to be able create a regular man, a regular woman, an insecure man or an insecure woman.
Adding another stipulation, lets say that insecurity modifies the existing methods of a person in some way, as opposed to just adding new methods.
I don’t want multiple inheritance since I am modifying existing methods of the class, not adding new methods that the class does not have. I also don’t want a decorator, as we will see later, because there are non-public methods called internally that I want overridden as well. I don’t want to put IF statements in the code to check if we are insecure, since I think inheritance is more maintainable. I also don’t want to create insecure man and insecure woman classes separately because they would share exactly the same code.
What I really want…
PERSON
|
------------------------------
| |
MAN WOMAN
| |
INSECURE INSECURE
In programming languages I’ve used, it is not possible to create the class hierarchy above at design time. The “insecure” class has two different parents, but only one parent at a time.
What if we could separate the definition of the class hierarchy from the class definition itself? e.g. class InsecureMan = Person -> Man -> Insecure
class InsecureWoman = Person -> Woman -> Insecure
In python, you can approximate such a language construct because you can create and manipulate classes on the fly. It is likely that someone better versed in metaclasses could do a cleaner job than I have done, but I include here a version that has been working for me.
Lets say I start with Insecure derived from Man. It is not safe to just take an instance of the Insecure class and change it’s parent class from Man to Woman if you desire an Insecure Woman instance. If you had existing instances of Insecure Men you will have transgendered them by changing the parent of the Insecure class, since they all share the same class definition. Therefore, before linking classes together to build a class heirarchy we create a class shell whose parent we can set, then copy over references to all the class members and methods.
Alternative Designs:
There are ways via composition to solve this particular problem. You could provide the person with a VoiceBox, or an InsecureVoiceBox derived from a VoiceBox. The InsecureVoiceBox adds in the “if that is OK.” when asked to speak. But, if there are a wide variety of methods that need modification, or if the voice box needed access to Person methods composition might not fit as well.
Now you could do this in a (still not) clean(er) way, using
.__class__
:The important thing is that you'll lose all class variables (but not instance variables!) changing the
__class__
attribute.You could instead use multiple inheritance. It is possible to derive from a primary base class and a secondary mixin class that just provides some added functionality. In Python you can use
super()
so the mixin class can still call the other base class even though it doesn't know which one it is. You'd have to derive fromobject
in Python versions < 3.0. Here is your example:super()
finds the next class in the MRO (method resolution order). The MRO is a list of all base classes of a class. The order of classes in the MRO depends on how you list the base classes in the class statement. For e.g. you can try this:Here's another mixin:
Now you can combine either one or both mixins with either
Man
orWoman
. If you use both mixins together, one mixin will end up calling the other.Yes, but then you use the dynamics. Anyways, I don't think you even need that weird stuff using python :)
Dammit,
s/use/lose/
Well, you can also at runtime change the bases attribute, there are a few quicks with it but it can work if done right: class A(object): pass
tada!
I like the mixins. One thing to keep in mind with the bases trick above, is that I don't want to change the class heirarchy of any object already instantiated. If I want to create one object that is a C derived from a B, and another that is a C derived from an D, I don't want to mess up that first object's inheritance tree when I go about setting up the second object. If I modify the bases of C, I will be messing with any object already created from from class C.