SoFunction
Updated on 2024-11-21

Detailed explanation of how Python implements issubclass

It's easy to detect whether a class is a subclass of another class using Python's built-in issubclass method.

This is the documentation for issubclass:

issubclass(class, classinfo)

Return true if class is a subclass (direct, indirect or virtual) of classinfo. A class is considered a subclass of itself. classinfo may be a tuple of class objects, in which case every entry in classinfo will be checked. In any other case, a TypeError exception is raised.

Subclasses of a class can be direct, indirect, or virtual.

The second argument to issubclass, classinfo, can be either a class object or a tuple containing a class object (either of which returns True if detected successfully).

Some examples of use:

>>> class A(object):
...   pass
...
>>> class B(A):
...   pass
...
>>> class C(B, A):
...   pass
...
>>> class D(C):
...   pass
...
>>> issubclass(D, D), issubclass(D, C), issubclass(D, B), issubclass(D, A), issubclass(D, object)
(True, True, True, True, True)
>>> D.__bases__
(<class '__main__.C'>,)
>>> D.__mro__
(<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)

D is a subclass of D. The base class of D when it was defined was C, so D is a subclass of C. And D is an indirect subclass of B, A, and object.

__mro__ is a class attribute. After the class is defined, the Python parser saves all the parents in a tuple in method resolution order as class attributes using a C3 algorithm.

So issubclass can be implemented simply like this:

def issubclass(cls, classinfo):
  if classinfo in cls.__mro__:
    return True
  return False

Python's issubclass is a built-in function (usually a C implementation), and is actually much more complicated to detect the type of the arguments, e.g., the first argument must be of type type, and the second argument of type or tuple. It is also important to consider whether the class is a virtual subclass, and a subclass of a subclass.

Example:

>>> from collections import abc
>>> class E:
...   def __len__(self):
...     return 1
...
>>> issubclass(E, )
True
>>> E.__mro__
(<class '__main__.E'>, <class 'object'>)
>>> class F:
...   pass
...
>>> issubclass(F, )
False
>>> (F)
<class '__main__.F'>
>>> issubclass(F, )
True

Python is a dynamically typed language, and has long used the Duck type form of programming, where it doesn't matter what type the object is, as long as it implements the required methods.

Now that we have ABCs, we can use them to determine whether a class or object is a subclass or instance of ABCs, but the class doesn't need to be shown as inheriting from ABCs, because python's built-in ABCs has a registration mechanism that registers a class as a subclass of it. This is because python's built-in ABCs have a registration mechanism for registering a class as a subclass.

There is also a mechanism to customize a __subclasshook__ method to recognize a certain type of class as a subclass.

The __subclasshook__ for e.g. looks like this:

@classmethod
def __subclasshook__(cls, C):
  if cls is Sized:
    if any("__len__" in B.__dict__ for B in C.__mro__):
      return True
  return NotImplemented

So class E with the __len__ method is a subclass of class E. This __subclasshook__ method is called through the __subclasscheck__ method, which is a method that every ABC class has, and is implemented in the ABCMeta class (which all other ABC classes inherit from).

The current implementation of the issubclass function first determines whether classinfo has a __subclasscheck__ method, and if it does, the logic for determining the subclass is returned by that method, i.e., it overrides the implementation of issubclass (CPython).

The __subclasscheck__ will be judged in several steps:

  1. Call the __subclasshook__ method, if there is a method definition
  2. Check if you are in the __mro__ list of the class to be tested
  3. Recursively check if the class to be tested is in a registered subclass (built-in _abc_registry list attribute)
  4. Recursively check if the class to be tested is a subclass of its own subclasses

The exact source code is in:/python/cpython/blob/3.6/Lib/#L194-L231

The related CPython implementation is at:/python/cpython/blob/0ccc0f6c7495be9043300e22d8f38e6d65e8884f/Objects/#L2223

And basically the implementation of the isinstance(object, classinfo) method simply calls issubclass(type(object), classinfo)

Reference:

29.7. abc — Abstract Base Classes : /3/library/
PEP 3119 – Introducing Abstract Base Classes: /dev/peps/pep-3119/

This is the entire content of this article.