python3: bind method to class instance with .__get__(), it works but why?
descriptor, methods, monkeypatching, python, python-3.x
Solution
The information you're looking for is in the Descriptor HowTo Guide:
To support method calls, functions include the `__get__()` method for binding methods during attribute access. This means that all functions are non-data descriptors which return bound or unbound methods depending whether they are invoked from an object or a class. In pure Python, it works like this:
class Function(object):
. . .
def __get__(self, obj, objtype=None):
"Simulate func_descr_get() in Objects/funcobject.c"
return types.MethodType(self, obj)
So there really isn't anything strange going on -- the `__get__` method of a function object calls `types.MethodType` and returns the result.
Problem
I know if you want to add a method to a class instance you can't do a simple assignment like this: ``` >>> def print_var(self): # method to be added print(self.var) >>> class MyClass: var = 5 >>> c = MyClass() >>> c.print_var = print_var ``` this indeed would cause `print_var` to behave like a normal function, so the `self` argument wouldn't have his typical meaning: ``` >>> c.print_var <function print_var at 0x98e86ec> >>> c.print_var() Traceback (most recent call last): File "<pyshell#149>", line 1, in <module> c.print_var() TypeError: print_var() takes exactly 1 argument (0 given) ``` In order to let the function be considered a method (i.e. to bind it to the instance), I used to use this code: ``` >>> import types >>> c.print_var = types.MethodType(print_var, c) >>> c.print_var <bound method MyClass.print_var of <__main__.MyClass object at 0x98a1bac>> >>> c.print_var() 5 ``` but I found that `.__get__` may also be used for this purpose: ``` >>> c.print_var = print_var.__get__(c) >>> c.print_var <bound method MyClass.print_var of <__main__.MyClass object at 0x98a1bac>> >>> c.print_var() 5 ``` The problem here is that it just works, but I can't understand how and why. The documentation about `.__get__` doesn't seem to help very much. I'd appreciate if someone could clarify this behaviour of python's interpreter.