Search code examples
pythonpython-2.xmonkeypatchinginstance-methods

How to monkeypatch one class's instance method to another one?


Given a class A I can simply add an instancemethod a via

def a(self):
    pass

A.a = a

However, if I try to add another class B's instancemethod b, i.e. A.b = B.b, the attempt at calling A().b() yields a

TypeError: unbound method b() must be called with B instance as first argument (got nothing instead)

(while B().b() does fine). Indeed there is a difference between

A.a -> <unbound method A.a>
A.b -> <unbound method B.b>  # should be A.b, not B.b

So,

  • How to fix this?
  • Why is it this way? It doesn't seem intuitive, but usually Guido has some good reasons...

Curiously enough, this no longer fails in Python3...


Solution

  • Let's:

    class A(object): pass
    
    class B(object):
        def b(self): 
            print 'self class: ' + self.__class__.__name__
    

    When you are doing:

    A.b = B.b
    

    You are not attaching a function to A, but an unbound method. In consequence python only add it as a standard attribute and do not convert it to a A-unbounded method. The solution is simple, attach the underlying function :

    A.b = B.b.__func__
    
    print A.b
        # print: <unbound method A.b>
    a = A()
    a.b()
        # print: self class: A
    

    I don't know all the difference between unbound methods and functions (only that the first contains the second), neither how all of that work internally. So I cannot explain the reason of it. My understanding is that a method object (bound or not) requires more information and functionalities than a functions, but it needs one to execute.

    I would agree that automating this (changing the class of an unbound method) could be a good choice, but I can find reasons not to. It is thus surprising that python 3 differs from python 2. I'd like to find out the reason of this choice.