
Abstract base classes
Python has this idea of abstract base classes (ABCs), which define a set of methods and properties that a class must implement in order to be considered a duck-type instance of that class. The class can extend the abstract base class itself in order to be used as an instance of that class, but it must supply all the appropriate methods.
Case study
Recently, I worked on an internal tool to parse source files containing test cases written in three different kinds of testing frameworks i.e. pytest, Robot Framework, and Cucumber. Since the tool needs to parse source files with three different parsers with a same set of APIs, it is advisable to create an abstract base class in this case to document what APIs the parsers should provide (documentation is one of the stronger use cases for ABCs). The abc module provides the tools I need to do this, as demonstrated in the following block of code:
from abc import ABC, abstractmethod
from pathlib import Path
class Parser(ABC):
@property
def name(self) -> str:
return self._name
@property
def test_case_indicator(self) -> str:
return self._test_case_indicator
@abstractmethod
def filter_test_case_from_source_file(self, path: Path) -> str:
pass
class PytestParser(Parser):
def __init__(self, source_file: Path) -> None:
self._name = 'pytest'
self._file_ext = 'py'
self._test_case_indicator = 'def '
def filter_test_case_from_source_file(self, path: Path) -> str:
pass
class RobotParser(Parser):
def __init__(self, source_file: Path) -> None:
self._name = 'robot'
self._file_ext = 'robot'
self._test_case_indicator = '*** Test Cases ***'
def filter_test_case_from_source_file(self, path: Path) -> str:
pass
class GherkinParser(Parser):
def __init__(self, source_file: Path) -> None:
self._name = 'gherkin'
self._file_ext = 'feature'
self._test_case_indicator = 'Scenario: '
def filter_test_case_from_source_file(self, path: Path) -> str:
pass
Though I omitted the implementation of the filter_test_case_from_source_file() method for brevity, the idea is that any subclass of the Parser class must implement this method, as it is decorated with @abstractmethod.
More common object-oriented languages (like Java, Kotlin) have a clear separation between the interface and the implementation of a class. For example, some languages provide an explicit interface keyword that allows us to define the methods that a class must have without any implementation. In such an environment, an abstract class is one that provides both an interface and a concrete implementation of some, but not all, methods. Any class can explicitly state that it implements a given interface.
Python’s ABCs help to supply the functionality of interfaces without compromising on the benefits of duck typing.