Explain in detail how Python implements issubclass

  • 2021-07-26 08:34:00
  • OfStack

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

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 1 class can be direct, indirect, or virtual.

The second parameter of issubclass, classinfo, can be either a class object or an tuple containing a class object (True is returned if one of them is detected successfully).

1 Some usage examples:


>>> 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, and the base class of D is 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, Python parser saves all parent classes in a tuple in the order of method resolution order through an C3 algorithm, and becomes a class attribute.

So issubclass can be implemented as simply as this:


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

issubclass of Python is a built-in function (1 is generally an C implementation), which is actually much more complicated. To detect parameter types, for example, the first parameter must be type type, and the second parameter must be type type or tuple type. Also consider whether the class is a virtual subclass and a subclass of the subclass.

For example:


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

Python is a dynamically typed language and has long been programmed in the form of Duck type (duck type), regardless of the object type, as long as the required methods are implemented.

Now with ABCs, it can be used to judge whether a class or an object is a subclass or instance of ABCs, but this class does not need to be displayed and inherited from ABCs, because ABCs built in python has a registration mechanism to register a class as its subclass. The register method as in the above example.

Another mechanism is to customize a __subclasshook__ method to identify a class of a certain type as a subclass.

For example, __subclasshook__ of abc. Sized 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

Therefore, the E class with the __len__ method is a subclass of abc. Sized, which is called through the __subclasscheck__ method, which is a method for every ABC class and implemented in the ABCMeta class (from which other ABC classes inherit).

In the current implementation of issubclass function, it will first judge whether classinfo has __subclasscheck__ method. If there is such a method, it will judge that the logic of subclass is returned by this method, that is, it will override the implementation of issubclass (CPython).

__subclasscheck__ is judged in several steps:

Call the __subclasshook__ method, if there is a method definition Check whether you are in the __mro__ list of classes to be detected Recursively check whether the class to be detected is a registered subclass (built-in _abc_registry list attribute) Recursively check whether the class to be detected is a subclass of its own subclass

The specific source code is: https://github.com/python/cpython/blob/3. 6/Lib/abc.py # L194-L231

The related CPython implementation is at: https://github.com/python/cpython/blob/0ccc0f6c7495be9043300e22d8f38e6ES138ES8884f/Objects/abstract. c # L2223

Basically, the implementation of isinstance (object, classinfo) method only needs to call issubclass (type (object), classinfo)

Reference:

29.7. abc-Abstract Base Classes: https://docs. python. org/3/library/abc. html
PEP 3119 Introducing Abstract Base Classes: https://www.python.org/dev/peps/pep-3119/


Related articles: