Search code examples
pythonpython-3.xinheritanceabc

How exactly does Python ABC interface work?


I was reading the book Fluent Python by Luciano Ramalho and I encountered the following line:

Note that the built-in concrete sequence types do not actually subclass the Sequence and MutableSequence abstract base classes (ABCs) depicted

So I looked into ABCs from Python docs and I am having problems understanding their purpose.

For example, isinstance([], abc.Iterable), returns True for lists, so does abc.Sequence and abc.MutableSequence which makes sense. And so does the following, for both isinstance and issubclass methods.

issubclass(str, abc.MutableSequence) --> False
isinstance((), abc.MutableSequence)  --> False

If I do the following:

class A:
    def __iter__(self):
        pass

a = A()
isinstance(a, abc.Iterable) # True
issubclass(A, abc.Iterable) # True

But A does not subclass abc.Iterable in its class definition (and, I am assuming neither does the other classes mentioned before, according to the book). How does the issubclass / isinstance methods and the ABC Interfaces work here? Do these functions simply look for dunder method signatures in the class definition and matches them with the dunder signatures from the mentioned ABC Class?

I don't see any purpose of the ABC classes other than providing a way of verifying whether a class contains some particular dunder methods, so it would be nice to know more about the purpose of this entire process.

Any kind of help would be appreciated. Thanks.


Solution

  • Iterable defines the __subclasshook__ class method to return True if the class has an __iter__ method.

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Iterable:
            return _check_methods(C, "__iter__")
        return NotImplemented
    

    There are two ways that a can be a subclass of b. Either a can inherit from b (or a subclass of b), or b.__subclasshook__(a) can return True.

    As isinstance(a, b) checks if type(a) is a subclass of b, it is also affected by the definition of __subclasshook__.