Inheritance in Python with Abstract Base Class(ABC)

Image for post
Image for post
Photo by freestocks on Unsplash

As a strongly typed dynamic language, python has no enforcement of interface like with static typed language such as Java. While it does introduce type annotations, but this can still be challenging when constructing more complex object such as a class for subclassing.

Luckily, protocol was introduced. It is similar to interfaces in that they define a collection of methods/attributes on an object and force the implementation to conform with the definition. But it is informal, meaning it is known as an accepted truth or defined in documentation but not strictly in code.

Protocols are widely supported and used especially in Python’s builtin classes.

For example, the popular iteration protocol or the sequence protocol . If you want to implement a certain protocol without conforming to its requirement, you might stumble into an error, like below with the desired sized protocol :

You can see above error due to the fact that there is no len method implemented on Member class. In oder for it to work, we need to adapt:

Well, this is a bit annoying, as we don’t want to have it every time raising an error before we realise certain methods are missing/do not conform. Also, it’s just very difficult to manage inheritance relationship without proper subclassing anyway. For example, a Member and SupermarketStock class can both can len(). But they are not the same thing even if they implement the same methods. And that’s where Abstract Base Classes(ABC) come to rescue.

Abstract Base Classes (ABCs)

Abstract base classes are a form of interface checking more strict than protocols. They are classes that contain abstract methods, which are methods declared but without implementation. ABCs are blueprint, cannot be instantiated, and require subclasses to provide implementations for the abstract methods.

In Python, we use the module ABC. ABC works by

  • defining an abstract base class, and ,
  • use concrete class implementing an abstract class either by
  • — register the class with the abc or,
  • — subclass directly from the abc

Let’s see an example first.

Subclassing

You can see we can’t implement Toast class without implementing the make_bread method.

Of course, even it’s not compulsory, you can add concrete implementation in the abc class, noting that it will be required to override in concrete class. This is a good way for subclasses to provide a custom logic.

ABC Metaclass, the old and new

Sometimes you may see another kind of using ABC class with ABCMeta :

So what’s the difference?

New class ABC has ABCMeta as its meta class. Using ABC as a base class has essentially the same effect as specifying metaclass=abc.ABCMeta, but is simpler to type and easier to read.
Update 3.4

So it’s the same. But since inheritance is more commonplace and more easily understood than __metaclass__, the abc module would benefit from a simple helper class:

Registering

The other way of using ABC is to register the concrete class thanks to the register method that can be invoked by its instance from the metaClass.

But you can see the problematic as it won’t force the implementation of abstractmethod defined on the ABC class.

That’s why it’s called “register subclass as a virtual subclass” by this way.

You may wonder, what is the use when we have class inheritance that enforce stricter checks already?

Per document, we can register unrelated concrete classes (even built-in classes) and unrelated ABCs as “virtual subclasses” — these and their descendants will be considered subclasses of the registering ABC by the built-in issubclass() function, but the registering ABC won’t show up in their MRO (Method Resolution Order) nor will method implementations defined by the registering ABC be callable (not even via super()).

So you see, virtual classes are used for instance when we want to make a class from a third-party package to be a subclass of our own abstract class. Since we can’t simply change its interface, we just register it and the intepreter will know this becomes a subclass of our own.

Note that we can simplify the register by using a decorator:

__subclasshook__

This method is one step further to register , as it defines whether the registered subclass is considered a subclass of this ABC. So you can customise the behaviour of issubclass without the need to call register() on every subclass. The important bit here is that it’s defined as classmethod on the class and it's called by abc.ABC.__subclasscheck__. So you can only use it if you're dealing with classes that have an ABCMeta metaclass.

It returns the following value:

  • True, the subclass is considered a subclass of this ABC;
  • False, the subclass is not considered a subclass of this ABC;
  • NotImplemented, the subclass check is continued with the normal flow.

So this way we can force any subclass to have certain method.

__abstractmethods__ and @abstractmethod

__abstractmethods__ is a descriptor to support ABC; it wraps a slot that is empty by default (so the descriptor raises an attribute error). Most of all, it is an implementation detail of how abstract methods are handled.

A class that has a metaclass derived from ABCMeta cannot be instantiated unless all of its abstract methods and properties are overridden. The abstract methods can be called using any of the normal ‘super’ call mechanisms.

Note that the abstractmethod() only affects subclasses derived using regular inheritance; “virtual subclasses” registered with register() method are not affected.

When abstractmethod() is applied in combination with other method descriptors, it should be applied as the innermost decorator:

That’s so much of it today!

Happy Reading!

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store