Getting a list of all the abstract base classes a given Python class implements

Viewed 551

Given a class (in Python 3.8), how can I get a list of all the abstract base classes it implments? For example, given list, I'm looking for something that would return Container, Iterable, Collection, Sized, Sequence, etc.

inspect.getmro(), __mro__, and __bases__ will provide the parent classes, but they do not include the abstract base classes. So for list they only provide object (and list itself for the mro ones).

isinstance will tell if an object implements one single specific abstract base class, but not a list of all abstract base classes it implements.

2 Answers

You can use collections.abc. According to python docs

This module provides abstract base classes that can be used to test whether a class provides a particular interface; for example, whether it is hashable or whether it is a mapping.

Link - https://docs.python.org/3/library/collections.abc.html

In [22]: import collections.abc

In [23]: import inspect

In [31]: [(k,v) for k, v in vars(collections.abc).items() if inspect.isclass(v) and issubclass(list, v) ]
Out[31]:
[('Iterable', collections.abc.Iterable),
 ('Reversible', collections.abc.Reversible),
 ('Sized', collections.abc.Sized),
 ('Container', collections.abc.Container),
 ('Collection', collections.abc.Collection),
 ('Sequence', collections.abc.Sequence),
 ('MutableSequence', collections.abc.MutableSequence)]

This isn't possible because of the way Python handles duck typing in abstract base classes. Using __subclasshook__ an abc can specify any arbitrary logic to determine if a given class is a subclass of it. For example, from the__subclasshook__ documentation:

class MyIterable(ABC):
    # <snipped>
    @classmethod
    def __subclasshook__(cls, C):
        if cls is MyIterable:
            if any("__iter__" in B.__dict__ for B in C.__mro__):
                return True
        return NotImplemented

This code says that the class C is a subclass of MyIterable if it contains the method __iter__. If you have an arbitrary class X that contains __iter__ it is a subclass of MyIterable even though X never expressly extends or is even aware of the existence of MyIterable. Therefore, the only way to discover if a given class is a subclass of a set of abstract classes is to check each of them using issubclass.

Given that the "subset of abstract bases classes" that you want to check is the set of all abstract base classes, and since there can be an infinite number of arbitrary abstract base classes in the world, it is not possible to test them all.

Of course, this is Python where nothing is truly private. It is technically possible to see all of the classes that are currently imported into your code by recursively scanning sys.modules, and testing against all of them using issubclass, but I can't imagine this being feasible in any real-world program.

Related