I understand that "self" is implicitly passed as an argument to methods when they are called. I am learning about descriptors and understand how functions "become" methods, I understand (at least conceptually) that attributes are 'bound' to objects, i have read multiple articles online and Raymond Hettinger's Descriptor how to.
In the example below, though, I just don't understand where the reference to the instance of the Function f (i.e. the parameter "self" on __get__, for which an argument will be implicitly passed) comes from.
I contrast this with the self parameter on f itself, which I understand will get passed as an argument to parameter obj on __get__ when __get__ is called.
How does python know, when __get__ is called, what underlying function needs to be passed to MethodType? I know this is the argument which is passed to self, but how/when does this happen? I thought I understood how self worked when I learned about __get__, and how the __get__ on the function Type can either return a Function or a MethodType, but I'm getting super frustrated.
I hope this makes sense. Thank you
class Function:
def __get__(self, obj, objtype=None):
"Simulate func_descr_get() in Objects/funcobject.c"
if obj is None:
return self
return MethodType(self, obj)
class D:
def f(self, x):
return x
MethodTypeis not important. ;-)The keys here are the "descriptor" protocol, the
__get__and the__call__special methods.Python does implement a thing called "MethodType" that works to hold together the "raw" function that will be called as a method, the
selfargument which the function will receive, a proper representation (by implementing__repr__).It is the
__call__method that is executed when a method is invoked - at this occasion, it just prepends the instance it has been bound to (when a call to__get__created it) to the arguments it received and pass then down to the function. A user-created class can fulfill the exact same role as the existingMethodTypewithout ever creating one (although the Python runtime likely contain some optimizations that will make using a native function object'sgetand theMethodTypeinstance it creates more efficient, by creating some shortcuts.So, to recap: the usage of "." for attribute reference for retrieving a function will call it's
__get__method (the code for that is contained inobject.__getattribute__- a class overriding__getattribute__can customize this behavior.If
__get__was called on a class (MyClass.method), the second argument to it isNone, and in the case of functions, it just returns the function itself. (In old Python2, another kind of object, an "unbound method" existed. In Python 3, just the plain function, as defined in the class body, and requiring an explicitselffirst argument is returned). If__get__is called on an instance, the second argument is the instance itself - the instace ofMyClass, not the instance ofFunctionor other descriptor.The code bellow builds upon your example, but I add an extra class with the role of
InstanceMethodclass itself: you can then add "print" statements at will to better understand its workings, if you can't just by looking at the code and commentsUpon running the code above, as is in the interactive interpreter, m.test() returns
42