views:

1951

answers:

11

I've read that it is possible to add a method to an existing object (e.g. not in the class definition) in python, I think this is called Monkey Patching (or in some cases Duck Punching). I understand that it's not always a good decision to do so. But, how might one do this?

And if you don't know python, can your language of choice do this? If so, how?

UPDATE 8/04/2008 00:21:01 EST:

That looks like a good answer John Downey, I tried it but it appears that it ends up being not a true method. Your example defines the new patch function with an argument of self, but if you write actual code that way, the now patched class method asks for an argument named self (it doesn't automagically recognize it as the identity class, which is what would happen if defined within the class definition), meaning you have to call class.patch(class) instead of just class.patch() if you want the same functionality as a true method. It looks like python isn't really treating it as a method, but more just as a variable which happens to be a function (and as such is callable). Is there any way to attach an actual method to a class?

Oh, and Ryan, that isn't exactly what I was looking for (it isn't builtin functionality), but it is quite cool nonetheless.

UPDATE 8/05/2008 22:24:01 EST:

Thank you for the well presented answer Jason Pratt, that was exactly what I was looking for.

+4  A: 

In Python monkey patching generally works by overwriting a class or functions signature with your own. Below is an example from the Zope Wiki:

from SomeOtherProduct.SomeModule import SomeClass
def speak(self):
return "ook ook eee eee eee!"
SomeClass.speak = speak

That code will overwrite/create a method called speak on the class. In Jeff Atwood's recent post on monkey patching. He shows an example in C# 3.0 which is the current language I use for work.

John Downey
That modifies the class, but it doesn't modify existing instances. For that, you need import types; myinstance.newmethodname = types.MethodType(mymethodname, myinstance).
Troy J. Farrell
+2  A: 

I don't know Python syntax, but I know Ruby can do it, and it is rather trivial. Let's say you want to add a method to Array that prints the length to standard out:

class Array
def print_length
puts length
end
end

If you don't want to modify the whole class, you can just add the method to a single instance of the array, and no other arrays will have the method:

array = [1, 2, 3]
def array.print_length
puts length
end

Just be aware of the issues involved in using this feature. Jeff Atwood actually wrote about it not too long ago.

Mike Stone
A: 

(I'm invoking the "I don't know Python" line here.) In Perl, you can create a package (which is how Perl implements the concept of a class) with the methods you want to patch in, then add that package to the @ISA of the package you want to patch. Heck, you can even edit the target package's symbol table directly.

Chris Jester-Young
+3  A: 

I also don't know Python, but this struck me as something that should be rather easy to find in Google.

I was right. (Note, I don't actually know if this is what you want, but it sure sounds like it.)

Ryan Fox
A: 

Sadly, I don't think you can do this in PHP...

Kevin
+2  A: 

@kevin d

You actually can do monkey patching in PHP provided you have the runkit extension. Here is an example right out of the PHP runkit documentation:

<?php
class Example {
    function foo() {
        return "foo!\n";
    }
}

// create an Example object
$e = new Example();

// output Example::foo() (before redefine)
echo "Before: " . $e->foo();

// Redefine the 'foo' method
runkit_method_redefine(
    'Example',
    'foo',
    '',
    'return "bar!\n";',
    RUNKIT_ACC_PUBLIC
);

// output Example::foo() (after redefine)
echo "After: " . $e->foo();
?>
John Downey
+34  A: 

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 properties 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 property 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 assigned as a property:

>>> a.barFighters
<function barFighters at 0x00A98EF0>

To bind it, we can use the instancemethod function in the new module:

>>> import new
>>> a.barFighters = new.instancemethod( barFighters, a, A )
>>> a.barFighters
<bound method A.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'

More information can be found by reading about descriptors and metaclass programming.

Jason Pratt
Excellently crafted answer -- thanks!
Pat Notz
`help(new)` says that `types.MethodType` should be used instead of `new.instancemethod` (though the latter one is more readable).
J.F. Sebastian
Depreciated since 2.6
Casebash
A: 

@John

I usually stay away from experimental functions... Hopefully they'll add more support for this in later versions of PHP.

Kevin
+1  A: 

What you're looking for is setattr I believe. Use this to set an attribute on an object.

>>> def printme(s): print repr(s)
>>> class A: pass
>>> setattr(A,'printme',printme)
>>> a = A()
>>> a.printme() # s becomes the implicit 'self' variable
< __ main __ . A instance at 0xABCDEFG>
HS
+1  A: 

What Jason Pratt posted is correct.

>>> class Test(object):
...   def a(self):
...     pass
... 
>>> def b(self):
...   pass
... 
>>> Test.b = b
>>> type(b)
<type 'function'>
>>> type(Test.a)
<type 'instancemethod'>
>>> type(Test.b)
<type 'instancemethod'>

As you can see, Python doesn't consider b() any different than a(). In Python all methods are just variables that happen to be functions.

Acuminate
+7  A: 

Module new is deprecated since python 2.6 and removed in 3.0, use types

see docs.python.org/library/new.html

In the example below I've deliberately removed return value from patch_me() function. 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'>
Evgeny