Search code examples
pythonclassinheritancemetaclassabc

If Python builtins derive from ABCs, then why is their metaclass type instead of ABCMeta?


I was reading PEP-3119, and I discovered that builtins derive from ABCs.

From PEP-3119:

The built-in type set derives from MutableSet. The built-in type frozenset derives from Set and Hashable.

In Python:

from collections.abc import Mapping
>>> print('y' if issubclass(dict, Mapping) else 'n')
y

With the above, I would assume that dict's metaclass should be ABCMeta since it has the ABCMeta metaclass in its inheritance chain. (dict > MutableMapping > Mapping). This might be where my knowledge is failing. Here's the relevant portion from the Python Data Model:

3.3.3.3. Determining the appropriate metaclass¶ The appropriate metaclass for a class definition is determined as follows: if no bases and no explicit metaclass are given, then type() is used; if an explicit metaclass is given and it is not an instance of type(), then it is used directly as the metaclass; if an instance of type() is given as the explicit metaclass, or bases are defined, then the most derived metaclass is used. The most derived metaclass is selected from the explicitly specified metaclass (if any) and the metaclasses (i.e. type(cls)) of all specified base classes. The most derived metaclass is one which is a subtype of all of these candidate metaclasses. If none of the candidate metaclasses meets that criterion, then the class definition will fail with TypeError.

However, Python shows dict's metaclass is type:

>>> type(dict)
<class 'type'>

Can someone explain this to me? Thank you.


Solution

  • The built-ins don't literally derive from any of the ABCs, they're registered as virtual subclasses of them. If you ask isinstance(dict, collections.abc.Mapping) it will return True (thanks to hooks that let you override subclass checking behavior), but collections.abc.Mapping will not appear in the MRO (Method Resolution Order, the complete set of parent classes checked for methods when you try to call a method, also used for delegation via super()).

    All that really happened is that, internally, collections.abc.Mapping is storing a set of registered virtual subclasses, and if you ask it if one of them is a subclass of Mapping, it will lie as a convenience (because by being registered as a virtual subclass, you've indicated that you in fact obey all the duck-typing requirements of the ABC).