Difference between slot wrapper, method wrapper and wrapper descriptor

When we do,

True.__init__

we get,

<method-wrapper '__init__' of bool object at 0x55abe921e100>

whereas,

list.__init__

gives,

<slot wrapper '__init__' of 'list' objects>
type(True.__init__)

gives,

method-wrapper

whereas,

type(list.__init__)

gives,

wrapper_descriptor

if we check this for builtins,

from collections import defaultdict
import keyword, re

d = defaultdict(set)
for i, j in keyword.__builtins__.items():
  if not re.search('Error|Warning|__|ipython', i):
    d[type(eval(i).__init__)].add(i)
print(d)

gives,

defaultdict(<class 'set'>,
            {<class 'method'>: {'credits', 'copyright', 'license'},
             <class 'method-wrapper'>: {'Ellipsis', 'False', 'None',
                                        'NotImplemented', 'True', 'abs', 'all',
                                        'any', 'ascii', 'bin', 'breakpoint',
                                        'callable', 'chr', 'compile', 'delattr',
                                        'dir', 'display', 'divmod', 'dreload',
                                        'eval', 'exec', 'execfile', 'format',
                                        'getattr', 'globals', 'hasattr', 'hash',
                                        'help', 'hex', 'id', 'input',
                                        'isinstance', 'issubclass', 'iter',
                                        'len', 'locals', 'max', 'min', 'next',
                                        'oct', 'open', 'ord', 'pow', 'print',
                                        'repr', 'round', 'runfile', 'setattr',
                                        'sorted', 'sum', 'vars'},
             <class 'wrapper_descriptor'>: {'BaseException', 'Exception',
                                            'GeneratorExit',
                                            'KeyboardInterrupt',
                                            'StopAsyncIteration',
                                            'StopIteration', 'SystemExit',
                                            'bool', 'bytearray', 'bytes',
                                            'classmethod', 'complex', 'dict',
                                            'enumerate', 'filter', 'float',
                                            'frozenset', 'int', 'list', 'map',
                                            'memoryview', 'object', 'property',
                                            'range', 'reversed', 'set', 'slice',
                                            'staticmethod', 'str', 'super',
                                            'tuple', 'type', 'zip'}})

what could be going on here?

In CPython, a slot wrapper is an instance of “wrapper_descriptor” that wraps calling a slot of a builtin type, such as tp_init. A slot wrapper may be inherited from a base class, such as object. When accessed as an attribute of an instance, a slot wrapper binds to the instance as a “method-wrapper”. For example:

>>> int.__init__ # inherited from object
<slot wrapper '__init__' of 'object' objects>
>>> type(int.__init__)
<class 'wrapper_descriptor'>

>>> n = 42
>>> method = n.__init__
>>> type(method)
<class 'method-wrapper'>
>>> method.__self__ is n
True

>>> # bind the method manually using the descriptor __get__() method:
>>> method = int.__init__.__get__(n)
>>> type(method)
<class 'method-wrapper'>
>>> method.__self__ is n
True

just found out there is one more, method_descriptor.

int.__dir__
<method '__dir__' of 'object' objects>
type(int.__dir__)
method_descriptor

for int,
method_descriptor includes,

<class 'method_descriptor'>: 
{'__ceil__', '__dir__', '__floor__', '__format__', '__getnewargs__',
 '__reduce__', '__reduce_ex__', '__round__',
 '__sizeof__', '__trunc__', 'bit_length', 'conjugate', 'to_bytes'}
x = 1
x.__dir__

gives,

<function int.__dir__>
type(x.__dir__)
builtin_function_or_method

and one more,

<class 'getset_descriptor'>: {'denominator', 'imag', 'numerator', 'real'}

which is obtained by something like,

type(int.denominator)

found out one more,

type(functools.partial.keywords)
member_descriptor

which is there for all three of these, for functools.partial

<class 'member_descriptor'>: {'keywords', 'args', 'func'}

I typed dir(inspect) to shell for some reason and got some results related to your post. Some of these results below may make you interested:

'isabstract', 'isasyncgen', 'isasyncgenfunction', 'isawaitable', 'isbuiltin', 'isclass', 'iscode',
 'iscoroutine', 'iscoroutinefunction', 'isdatadescriptor', 'isframe', 'isfunction', 'isgenerator', 
'isgeneratorfunction', 'isgetsetdescriptor', 'ismemberdescriptor', 'ismethod', 
'ismethoddescriptor', 'ismodule', 'isroutine', 'istraceback'

Their __doc__s doesn’t say much but may be helpful:

>>> inspect.ismemberdescriptor.__doc__
'Return true if the object is a member descriptor.\n\n        Member descriptors are specialized descriptors defined in extension\n        modules.'
>>> inspect.isgetsetdescriptor.__doc__
'Return true if the object is a getset descriptor.\n\n        getset descriptors are specialized descriptors defined in extension\n        modules.'

ETC.

Some more info here.

Probably related:

You can have a look at inspect's source code -it is really easy to understand- to get some general sight about those descriptor and wrapper objects (method_descriptor etc.). Generally found at (I am using 3.9):

C:\Users\YOUR_NAME\AppData\Local\Programs\Python\Python39\Lib\inspect.py

And, I found this code for you (belongs to types module):

WrapperDescriptorType = type(object.__init__)
MethodWrapperType = type(object().__str__)
MethodDescriptorType = type(str.join)
ClassMethodDescriptorType = type(dict.__dict__['fromkeys'])

Hope, this post helped you a bit.

yes, I checked the inspect module, but it still is a bit confusing to me which category a particular method belongs to, as there appear to be a few too many options, and the difference between them is not well documented.

Why do you care?

If you’re just curious, that’s okay. But if you think that this is something that you have to learn before you can use Python, no, that is absolutely not the case.

The different “slot wrapper” and “method wrapper” types are not part of the Python language. They are purely internal implementation details which happen to be visible. As an ordinary Python programmer, you will never need to care whether a certain built-in is a slot-wrapper, method-wrapper, “builtin_method_or_function”, or something else.

The differences between them are not well documented because the differences are not part of the Python language. If you use a different version, or a different interpreter, like IronPython, Jython, PyPy etc, those types may change or disappear entirely.

Generally speaking, if the name of the type is not a legal Python identifier, such as “slot wrapper” (space) and “method-wrapper” (hyphen), then you probably don’t need to care about it.