Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 721
Gerenuk Avatar asked May 15 '11 18:05

Gerenuk


2 Answers

del a deletes the name 'a' from the namespace, but not the object referenced by that name. See this:

>>> x = 7
>>> y = x
>>> del x
>>> print y
7

Also, some_object.__del__ is not guaranteed to be called at all.

Also, I already answered your question here (in german).

like image 118
pillmuncher Avatar answered Nov 10 '22 17:11

pillmuncher


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
like image 38
Boaz Yaniv Avatar answered Nov 10 '22 18:11

Boaz Yaniv