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.

Abhishek Bhatia
  • 9,404
  • 26
  • 87
  • 142

2 Answers2

10

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.

Community
  • 1
  • 1
taleinat
  • 8,441
  • 1
  • 30
  • 44
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).

mgilson
  • 300,191
  • 65
  • 633
  • 696
  • Thanks! Is there any case where a descriptor protocol won't be necessary for monkey patching. In this example, https://github.com/yasoob/intermediatePython/blob/master/args_and_kwargs.rst#when-to-use-them the guy doesn't use it and it still works. – Abhishek Bhatia Jul 20 '16 at 16:35
  • @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`) – mgilson Jul 20 '16 at 16:42
  • Thanks! Can you explain why you advised to create a separate library. Not sure I understand you. – Abhishek Bhatia Jul 20 '16 at 16:48
  • @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`](https://docs.python.org/3/library/unittest.mock.html) (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. – mgilson Jul 20 '16 at 16:51