Online Shopping : Computers : Programming : Languages : Python : Snippets

+ Search
Add Entry AlertManage Folder Edit Entry Add page to
Did You Find This Entry Useful?

2 of 2 people (100%) answered Yes
Recently 1 of 1 people (100%) answered Yes


Variable 'superclass'es?

Mar 31st, 2009 10:39
engatoo engatoo, Roj Merry, alex martin, Nathan Wallace, Hans Nowak, Snippet 168, Neel Krishnaswami

Packages: oop
>I think the word is 'superclass' Anyways.
>What I want is a class, that inherits from a variable class.
>See what i'm trying to do? Can it be done?
I see what you are doing to do. Two thoughts come to mind:
1. Python can do it, but the way you are doing it won't work.
2. However, it's probably a bad idea.
3. But it's fun to do anyway.
Let's deal with point one first. Your method won't work because of 
the way that Python resolves names. It does things by object reference,
when you write something like 
class Foo(Bar):
The class Foo inherits from the *object* pointed to by the name Bar, at
the time of initialization. (Remember that in Python, class and 
definitions are runtime statements.) This means that if you later 
Bar = Quux
then the superclass of Foo won't change. In order to do what you 
want, you'll need to write a factory function to create the class
dynamically. Here's what you need:
class Parent1:
class Parent2:
def ChildFactory(parent):
    class Child(parent):
    return Child
Testing this in the interpreter, we 
Child1 = ChildFactory(Parent1)
Child2 = ChildFactory(Parent2)
print Child1.__bases__
#(<class __main__.Parent1 at 80d5ce8>,)
print Child2.__bases__
#(<class __main__.Parent2 at 80d96f0>,)
The __bases__ special attribute of a class object displays the 
superclasses of that class. We can't assign to it, though, so
you can't change a class's superclasses that way. (Though you
can change an instance's class by assigning to its __class__
There are some funny side-effects to this approach that you
should remember when debugging. If you have two instances, 
each an instance of a different class produced by the factory
kid1 = Child1()
kid2 = Child2()
If you print the class name using the __class__ attribute you
will get the name of the class definition in the ChildFactory
print kid1.__class__
print kid2.__class__
This works ok when in actual code, though:
print kid2.__class__ == kid1.__class__
# 0
It's just that the visual messages can be a bit confusing.
Now, we can move on to point 2, why this is probably a bad idea.
In general, whenever you want to dynamically manipulate your 
class hierarchy, this is a sign that inheritance is the wrong
tool for the job. It's hard to read, and sometimes you will get
bitten by non-unique names inside the classes, and worst of 
all you will confuse other programmers (including yourself after
six months) because most everyone associates classes with static
things that don't change during the program run. 
Still, very often you need the dynamism? What to do -- use delegation
instead. Instead of inheriting from a class to create a new class at
runtime, you write a class that receives another object as an
initialization variable. Then each method can dispatch on the object's
class at runtime. 
class Foo:
    def frob(self):
        print 'Foo!'
class Bar:
    def fiddle(self):
        print 'Bar!'
class Child:
    def __init__(self, obj):
        self.obj = obj
    def method(self):
        """A method that dispatches based on self.obj's type."""
        if self.obj.__class__ == Foo:
        elif self.obj.__class__ == Bar:
kid1 = Child(Foo())
kid2 = Child(Bar())
Trying it out, we see:
Which is what we wanted. 
A third possibility (and you thought that only Perl had more than one 
to do it) is to create a metaclass that creates the child classes as
instances of itself. This isn't that hard either, but it would bloat 
post past my endurance. So someone else can post it.
All of these approaches are basically equivalent, btw. The question
is what is appropriate for your problem. And that is up to you. (Also, 
appreciate it if real Python gurus could correct any of the mistakes I
have certainly made.)