This recipe demonstrates a technique for adding functionality to an existing class method after the class has already been defined.
Requires python 2.1 or later.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
from __future__ import nested_scopes import new def enhance_method(klass, method_name, replacement): 'replace a method with an enhancement' method = getattr(klass, method_name) setattr(klass, method_name, new.instancemethod( lambda *args, **kwds: replacement(method, *args, **kwds), None, klass)) def method_logger(old_method, self, *args, **kwds): 'enhancement that logs all calls to a method' print '*** calling: %s%s, kwds=%s' % (old_method.__name__, args, kwds) return_value = old_method(self, *args, **kwds) # call the original method print '*** %s returns: %s' % (old_method.__name__, `return_value`) return return_value def demo(): class Deli: def order_cheese(self, cheese_type): print 'Sorry, we are completely out of %s' % cheese_type d = Deli() d.order_cheese('Gouda') enhance_method(Deli, 'order_cheese', method_logger) d.order_cheese('Cheddar')
This is useful when modifying the behavior of a standard or third party python module when changing the module itself is undesirable.
Also, this recipe can be very handy for debugging, since you can use it to log all calls to a library method that you want to watch (without changing the library code).
Sometimes one needs to globally change the behaviour of an entire third-party python library. For example, a python library that you downloaded has 50 different methods that all return error codes but you want these methods to raise exceptions instead (again, you don't want to change their code). After importing the offending module, you call enhance method to hook in a replacement function that checks the return value and issues an exception if an error occurred.
See also: "Class Adoption" recipe