24

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 = ???
4
  • possibly... I don't really understand what's going on in that code. Commented Jan 24, 2015 at 16:59
  • is my question "mocking" or "monkey-patching"? Commented Jan 24, 2015 at 17:00
  • Can you explain the difference, please? Commented Jan 24, 2015 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. Commented Jan 24, 2015 at 17:03

3 Answers 3

41

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'>
>>>
Sign up to request clarification or add additional context in comments.

3 Comments

Your solution also works well. I was just showing how to do it without importing types.MethodType. :)
Does using super() inside times_eight() work?
short: s.make = times_eight.__get__(s)
10

Doh!

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

Comments

8

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.)

3 Comments

I think better to add object to closure lambda arg, s=s: time_eight(s, arg)
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.
that lambda trick is a very clear way to monkey patch an instance, rather than the class. Bravo!