I've read that it is possible to add a method to an existing object (i.e., not in the class definition) in Python.
I understand that it's not always good to do so. But how might one do this?
In Python, there is a difference between functions and bound methods.
>>> def foo(): ... print "foo" ... >>> class A: ... def bar( self ): ... print "bar" ... >>> a = A() >>> foo <function foo at 0x00A98D70> >>> a.bar <bound method A.bar of <__main__.A instance at 0x00A9BC88>> >>>
Bound methods have been "bound" (how descriptive) to an instance, and that instance will be passed as the first argument whenever the method is called.
Callables that are attributes of a class (as opposed to an instance) are still unbound, though, so you can modify the class definition whenever you want:
>>> def fooFighters( self ): ... print "fooFighters" ... >>> A.fooFighters = fooFighters >>> a2 = A() >>> a2.fooFighters <bound method A.fooFighters of <__main__.A instance at 0x00A9BEB8>> >>> a2.fooFighters() fooFighters
Previously defined instances are updated as well (as long as they haven't overridden the attribute themselves):
>>> a.fooFighters() fooFighters
The problem comes when you want to attach a method to a single instance:
>>> def barFighters( self ): ... print "barFighters" ... >>> a.barFighters = barFighters >>> a.barFighters() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: barFighters() takes exactly 1 argument (0 given)
The function is not automatically bound when it's attached directly to an instance:
>>> a.barFighters <function barFighters at 0x00A98EF0>
To bind it, we can use the MethodType function in the types module:
>>> import types >>> a.barFighters = types.MethodType( barFighters, a ) >>> a.barFighters <bound method ?.barFighters of <__main__.A instance at 0x00A9BC88>> >>> a.barFighters() barFighters
This time other instances of the class have not been affected:
>>> a2.barFighters() Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: A instance has no attribute 'barFighters'
Module new is deprecated since python 2.6 and removed in 3.0, use types
In the example below I've deliberately removed return value from
I think that giving return value may make one believe that patch returns a new object, which is not true - it modifies the incoming one. Probably this can facilitate a more disciplined use of monkeypatching.
import types class A(object):#but seems to work for old style objects too pass def patch_me(target): def method(target,x): print "x=",x print "called from", target target.method = types.MethodType(method,target) #add more if needed a = A() print a #out: <__main__.A object at 0x2b73ac88bfd0> patch_me(a) #patch instance a.method(5) #out: x= 5 #out: called from <__main__.A object at 0x2b73ac88bfd0> patch_me(A) A.method(6) #can patch class too #out: x= 6 #out: called from <class '__main__.A'>