On non-builtin types, types.FunctionType
and types.MethodType
behave as expected:
>>> isinstance(MyClass.func, FunctionType)
True
>>> isinstance(MyClass.func, MethodType)
False
>>> isinstance(MyClass().func, FunctionType)
False
>>> isinstance(MyClass().func, MethodType)
True
However, on a built-in type, BuiltinFunctionType
and BuiltinMethodType
don't behave accordingly:
>>> isinstance(list.append, (FunctionType, MethodType,
... BuiltinFunctionType, BuiltinMethodType))
False <- Why??
>>> isinstance([].append, (FunctionType, MethodType))
False
>>> isinstance([].append, BuiltinFunctionType)
True <- Why??
>>> isinstance([].append, BuiltinMethodType)
True
Now this does't make sense, does it? Can someone explain?
EDIT: isinstance(list.append, MethodDescriptorType) == True
. Can someone explain how MethodDescriptorType
differs from BuiltinMethodType
and why do we need it? The official documentation doesn't really say much.
BuiltinFunctionType
and BuiltinMethodType
are just different names referring to the same object:
>>> BuiltinFunctionType is BuiltinMethodType
True
isinstance(list.append, BuiltinMethodType)
is false because it's not a method (i.e. an instance method bound to some object). The method binding happens through object.__getattribute__
, so the functions defined with a class are just plain functions:
>>> def func(x): pass
...
>>> class MyClass: func = func
...
>>> MyClass.func is func
True
and hence isinstance(MyClass.func, FunctionType)
. This works because all functions are non-data descriptors:
>>> func.__get__
<method-wrapper '__get__' of function object at 0x7f80abe57400>
For builtin types these objects (e.g. list.append
) are also dedicated descriptors that handle the binding to their objects. So x = []; x.append
returns a bound method which was created by the descriptor list.append
and bound to the object x
:
>>> type(list.append)
<class 'method_descriptor'>