Swapping superclass from a module

Arnaud Delobelle arnodel at googlemail.com
Sat May 16 15:17:30 EDT 2009


"Emanuele D'Arrigo" <manu3d at gmail.com> writes:

> Hi everybody,
>
> let's assume I have a module with loads of classes inheriting from one
> class, from the same module, i.e.:
>
> ## myFile.py
> class SuperClass(object)
> class SubClass1(SuperClass)
> class SubClass2(SuperClass)
> class SubClass3(SuperClass)
>
> In a separate file I also have:
>
> ## myOtherFile.py
> class NewSuperClass(object)
>
> Now, let's also assume that myFile.py cannot be changed or it's
> impractical to do so. Is there a way to replace the SuperClass at
> runtime, so that when I instantiate one of the subclasses
> NewSuperClass is used instead of the original SuperClass provided by
> the first module module?
>
> That was the generic case. Would the solution change much if
> NewSuperClass was actually inheriting from SuperClass, effectively
> wedging itself between the SuperClass and the SubClasses?
>

Here is an attempt to do this.  Imagine there is a module 'modfoo.py'
like this:

--------------------------------------------------
class Base(object):
    def bar(self):
        print 'Base.bar'
    
class A(Base):
    def bar(self): 
        Base.bar(self)
        print 'A.bar'

class B(Base):
    def bar(self):
        super(B, self).bar()
        print 'B.bar'
--------------------------------------------------

The following script will patch the module so that all subclasses of
Base in module modfoo have their new superclass changed.  I called it
'patchfoo.py'.

--------------------------------------------------
import modfoo

# This is the new base
class Wedge(modfoo.Base):
    def bar(self):
        super(Wedge, self).bar()
        print 'Wedge.bar'

# Insert Wedge into each subclass of modfoo.Base
for subclass in modfoo.Base.__subclasses__():
    if subclass.__module__ != 'modfoo': continue
    attrs = dict(item for item in subclass.__dict__.items() 
                      if item[0][:2] != '__')
    name = subclass.__name__
    setattr(modfoo, name, type(name, (Wedge,), attrs))

# Replace modfoo.Base with Wedge
modfoo.Base = Wedge

# Tests
a = modfoo.A()
b = modfoo.B()

for obj in 'a' ,'b':
        call = '%s.bar()' % (obj,)
        print '-'*5, call
        exec call
--------------------------------------------------

Now let's try it:

marigold:junk arno$ python patchfoo.py 
----- a.bar()
Base.bar
Wedge.bar
A.bar
----- b.bar()
Base.bar
Wedge.bar
B.bar

Of course, there are plenty of ways this could break.

-- 
Arnaud



More information about the Python-list mailing list