19

I'm trying to monkey-patch a class instance, but don't quite see how I can patch a class method no problem.

>>> class Simple(object): 
...     def make(self, arg):
...         return arg * 2
... 
>>> s = Simple()
>>> def times_four(self, arg):
...   return arg * 4
... 
>>> Simple.make = times_four
>>> s.make(10)
40

But say I only wanted to replace make in the instance, what's the easiest way to do that?

>>> def times_eight(self, arg):
...   return arg * 8
>>> s.make = ???
  • possibly... I don't really understand what's going on in that code. –  Jan 24 '15 at 16:59
  • is my question "mocking" or "monkey-patching"? –  Jan 24 '15 at 17:00
  • Can you explain the difference, please? – pss Jan 24 '15 at 17:03
  • 2
    Technically these terms are somewhat synonymous. The difference is in intentions - mocking is usually monkey-patching for the purpose of testing, whereas the latter, in its origins is more general. – BartoszKP Jan 24 '15 at 17:03

3 Answers3

34

You can create a new instance method out of times_eight by using its __get__ special method:

>>> class Simple(object):
...     def make(self, arg):
...         return arg * 2
...
>>> s = Simple()
>>> def times_eight(self, arg):
...     return arg * 8
...
>>> s.make = times_eight.__get__(s, Simple)
>>> s.make(10)
80
>>> type(s.make)
<type 'instancemethod'>
>>>
9

Doh!

>>> import types
>>> s.make = types.MethodType(times_eight, s, Simple)
>>> s.make(10)
80
5

Methods only get the self arg passed automatically when received from the class (not from the instance dict), so you'll need to pass an 1-arg function there:

s.make = lambda arg: times_eight(s, arg)

(You can simplify that with functools.partial.)

Kos
  • 70,399
  • 25
  • 169
  • 233
  • I think better to add object to closure `lambda arg, s=s: time_eight(s, arg)` – pss Jan 24 '15 at 17:04
  • 3
    This isn't really making an instance method, however. Right? It's a lambda or a partial, so I don't think this really works. –  Jan 24 '15 at 17:04
  • 1
    that lambda trick is a very clear way to monkey patch an instance, rather than the class. Bravo! – Tim Mar 02 '22 at 02:56