Swapping superclass from a module

Peter Otten __peter__ at web.de
Sun May 17 03:24:32 EDT 2009


Terry Reedy wrote:

> Steven D'Aprano wrote:
>> On Sat, 16 May 2009 09:55:39 -0700, Emanuele D'Arrigo wrote:
>> 
>>> Hi everybody,
>>>
>>> let's assume I have a module with loads of classes inheriting from one
>>> class, from the same module, i.e.:
>> [...]
>>> 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's called "monkey patching" or "duck punching".
>> 
>> http://en.wikipedia.org/wiki/Monkey_patch
>> 
>> http://wiki.zope.org/zope2/MonkeyPatch
>> 
>> http://everything2.com/title/monkey%2520patch
> 
> If the names of superclasses is resolved when classes are instantiated,
> the patching is easy.  If, as I would suspect, the names are resolved
> when the classes are created, before the module becomes available to the
> importing code, then much more careful and extensive patching would be
> required, if it is even possible.  (Objects in tuples cannot be
> replaced, and some attributes are not writable.)

It may be sufficient to patch the subclasses:

$ cat my_file.py
class Super(object):
    def __str__(self):
        return "old"

class Sub(Super):
    def __str__(self):
        return "Sub(%s)" % super(Sub, self).__str__()

class Other(object):
    pass

class SubSub(Sub, Other):
    def __str__(self):
        return "SubSub(%s)" % super(SubSub, self).__str__()

if __name__ == "__main__":
    print Sub()

$ cat main2.py
import my_file
OldSuper = my_file.Super

class NewSuper(OldSuper):
    def __str__(self):
        return "new" + super(NewSuper, self).__str__()

my_file.Super = NewSuper
for n, v in vars(my_file).iteritems():
    if v is not NewSuper:
        try:
            bases = v.__bases__
        except AttributeError:
            pass
        else:
            if OldSuper in bases:
                print "patching", n
                v.__bases__ = tuple(NewSuper if b is OldSuper else b
                                    for b in bases)


print my_file.Sub()
print my_file.SubSub()
$ python main2.py
patching Sub
Sub(newold)
SubSub(Sub(newold))

Peter




More information about the Python-list mailing list