python3: bind method to class instance with .__get__(), it works but why?
Asked Answered
J

1

10

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.

Jarietta answered 20/9, 2011 at 19:44 Comment(0)
F
9

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.

Fluorspar answered 20/9, 2011 at 20:30 Comment(5)
In Python 3 MethodType() only accepts 2 arguments.Notify
@Notify I'm quoting from the Python docs there, sounds like they need to be updated.Fluorspar
In fact they are updated. I recommend you edit the answerLoveinidleness
@J.C.Rocamonde um... I followed the link... 3 parameters in the python docs... unless I'm misunderstanding the intention of your comment?Diligence
@Diligence He's not saying __get__ changed, he's saying MethodType takes only two arguments now -- it used to take three. I updated the answer two years ago to reflect that.Fluorspar

© 2022 - 2024 — McMap. All rights reserved.