Search code examples
pythonmonkeypatching

Monkey patch __del__ to new function


For specific debugging purposes I'd like to wrap the del function of an arbitrary object to perform extra tasks like write the last value of the object to a file.

Ideally I want to write monkey(x) and it should mean that the final value of x is printed when x is deleted

Now I figured that del is a class method. So the following is a start:

class Test:
    def __str__(self):
        return "Test"

def p(self):
    print(str(self))

def monkey(x):
    x.__class__.__del__=p

a=Test()
monkey(a)
del a

However if I want to monkey specific objects only I suppose I need to dynamically rewrite their class to a new one?! Moreover I need to do this anyway, since I cannot access del of built-in types?

Anyone knows how to implement that?


Solution

  • While special 'double underscore' methods like __del__, __str__, __repr__, etc. can be monkey-patched on the instance level, they'll just be ignored, unless they are called directly (e.g., if you take Omnifarious's answer: del a won't print a thing, but a.__del__() would).

    If you still want to monkey patch a single instance a of class A at runtime, the solution is to dynamically create a class A1 which is derived from A, and then change a's class to the newly-created A1. Yes, this is possible, and a will behave as if nothing has changed - except that now it includes your monkey patched method.

    Here's a solution based on a generic function I wrote for another question: Python method resolution mystery

    def override(p, methods):
        oldType = type(p)
        newType = type(oldType.__name__ + "_Override", (oldType,), methods)
        p.__class__ = newType
    
    
    class Test(object):
        def __str__(self):
            return "Test"
    
    def p(self):
        print(str(self))
    
    def monkey(x):
        override(x, {"__del__": p})
    
    a=Test()
    b=Test()
    monkey(a)
    print "Deleting a:"
    del a
    print "Deleting b:"
    del b