Consider the following snippet.
import types
def deff(s):
print(f"deff called, {s=}")
lam = lambda s: print(f"lam called, {s=}")
class Clbl:
def __call__(s):
print(f"__call__ called, {s=}")
clbl = Clbl()
print(type(deff) == types.FunctionType) # True
print(type(lam) == types.LambdaType) # True
print(type(clbl) == Clbl) # True
class A:
deff = deff
lam = lam
clbl = clbl
a = A()
a.deff() # deff called, s=<__main__.A object at 0x7f8c242d4490>
a.lam() # lam called, s=<__main__.A object at 0x7f8c242d4490>
a.clbl() # __call__ called, s=<__main__.Clbl object at 0x7f8c24146730>
print(a.deff is deff) # False
print(a.lam is lam) # False
print(a.clbl is clbl) # True
print(type(a.deff) == types.FunctionType) # False
print(type(a.lam) == types.LambdaType) # False
print(type(a.clbl) == Clbl) # True
print(type(a.deff) == types.MethodType) # True
print(type(a.lam) == types.MethodType) # True
Why are a.deff and a.lam methods but a.clbl not a method? Why do the expression a.deff() and a.lam() pass a as the first argument to the corresponding functions while a.clbl() doesn’t?
>Solution :
TL;DR Clbl doesn’t implement the descriptor protocol; types.FunctionType does.
All three attempts to access attributes on a fail to find instance attributes by those names, and so the lookup proceeds to the class. All three lookups succeed on class attributes, at which point it is time to see which, if any of them, define a __get__ attribute.
a.clbl does not, so you get back the actual Clbl instance bound to the class attribute.
a.deff and a.lam do (there is no significant different in between the two; types.LambdaType itself is just an alias for types.FunctionType, because lambda expressions and def statements both create values of the same type). types.FunctionType.__get__ is what returns a callable method object, which his what manages the implicit passing of an invoking instance to the underlying function.
In short, a.deff() is equivalent to
A.__dict__['deff'].__get__(a, A)()
The method object does three things:
- Maintains a reference
__self__to the instancea - Maintains a reference
__func__to the functionA.deff - Implements a
__call__method that callsself.__func__onself.__self__and any other arguments passed to themethodinstance.