9
>>> class A:
... def foo(self):
... print(self)
...
>>>
>>> a = A()
>>> a.foo()
<__main__.A instance at 0x7f4399136cb0>
>>> def foo(self):
... print(self)
...
>>> a.foo = foo
>>> a.foo()
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
TypeError: foo() takes exactly 1 argument (0 given)

I am trying to understand monkey-patching in Python. Please illustrate the reason for the error and how to fix it.

asked Jul 20, 2016 at 15:47
0

2 Answers 2

11

As described in this SO answer, you need to use types.MethodType or something similar when doing this, e.g.:

a.foo = types.MethodType(foo, a)

The reason is that a.foo = foo just sets the function foo as an attribute of a - no "binding magic" is done. To have Python "magically" pass the instance as the first argument when calling a.foo, you need to tell Python to do such binding, e.g. by using types.MethodType.

See the above linked answer for (much) more details.

answered Jul 20, 2016 at 15:57
Sign up to request clarification or add additional context in comments.

Comments

3

So the tricky thing here is that what you get depends on where the method lives:

class A(object):
 def foo(self):
 print("Hello world")
def patch(self):
 print("patched!")
print(type(A.foo))
a = A()
print(type(a.foo))

If you run this, you'll get different results on python2.x and 3.x:

$ python ~/sandbox/test.py # python2.x
<type 'instancemethod'>
<type 'instancemethod'>
$ python3 ~/sandbox/test.py # python3.x
<class 'function' at 0x100228020>
<class 'method' at 0x10021d0c0>

But in either case it's clear that a.foo is a method of some sort.

What happens if we try to monkey patch it?

a.foo = patch
print(type(a.foo)) # <type 'function'> (2.x) / <class 'function'> (3.x)

Ok, now we see that a.foo is of type function (not a method). So the question is how do we make a method out of out "patch"? The answer is we use it's descriptor protocol when adding it as an attribute:

a.foo = patch.__get__(a, A)

For a method on a class, when you do a.some_method, python actually does: a.some_method.__get__(a, type(a)) so we're just reproducing that call sequence here (explicitly).

answered Jul 20, 2016 at 16:02

4 Comments

Thanks! Is there any case where a descriptor protocol won't be necessary for monkey patching. In this example, github.com/yasoob/intermediatePython/blob/master/… the guy doesn't use it and it still works.
@AbhishekBhatia -- you don't need it if you patch the class before you create the instance: A.foo = patch; a = A(); a.foo() -- However, if you're really going to be doing a lot of patching, I hope it's just in tests and I'd advise you use a mocking library to do it (e.g. mock)
Thanks! Can you explain why you advised to create a separate library. Not sure I understand you.
@AbhishekBhatia -- Not create. use :-). And the reason is because they've probably done most of the hard work for you already. I'd suggest unittest.mock (which can be downloaded/installed using pip if you're on python2.x). Obviously it's good to know how these things work for the occasions when you need them, but most of the time, you can use the library and not worry about the details.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.