Let's talk about an example:
Assume that you’re designing a music player application, intended to support multiple file formats. Some of the formats are known now, but some are not yet known. The idea is to design an abstract class representing a base music format and corresponding methods for “open”, “play”, “get details”, “rewind”, etc., to maintain polymorphism.
Your team should implement concrete classes for each format you'd like to support. Whenever new format specifications become available, you won’t have to rework your music player code, you’ll just have to deliver a class supporting the new file format, fulfilling the contract imposed by the abstract class.
Remember that it isn’t possible to instantiate an abstract class, and it needs subclasses to provide implementations for those abstract methods which are declared in the abstract classes. This behavior is a test performed by a dedicated Python module to validate if the developer has implemented a subclass that overrides all abstract methods.
When we’re designing large functional units, in the form of classes, we should use an abstract class. When we want to provide common implemented functionality for all implementations of the class, we could also use an abstract class, because abstract classes partially allow us to implement classes by delivering concrete definitions for some of the methods, not only declarations.
We have just defined the means by which to provide a common Application Program Interface (API) for a set of subclasses. This capability is especially useful in situations where your team or third-party is going to provide implementations, such as with plugins in an application, even after the main application development is finished.