How to Use Abstract Classes in Python | Towards Data Science

An abstract class is a class, but not one you can create objects from directly. Its purpose is to define how other classes should look like, i.e. what methods and properties they are expected to have.
The methods and properties defined (but not implemented) in an abstract class are called abstract methods and abstract properties. All abstract methods and properties need to be implemented in a child class in order to be able to create objects from it.
We can create an abstract class by inheriting from the ABC class which is part of the abc module.
from abc import (  ABC,  abstractmethod,)class BasicPokemon(ABC):  def __init__(self, name):    self.name = name    self._level = 1  @abstractmethod  def main_attack(self):    ...
In the code above, we create a new abstract class called BasicPokemon. We indicate that the method main_attack is an abstract method by using the decorator abstractmethod, which means we expect this to be implemented in every subclass of BasicPokemon.
What happens if you try and create an object from BasicPokemon directly? TypeError!
Now, you will notice that BasicPokemon‘s __init__ method expects a name, argument, therefore the code above will not have worked anyway. The thing to note here is that it doesn’t even go to the __init__ method to check that. The fact that it has an abstract method that has not been implemented takes precedence and it fails because of that!
This is how one would use the BasicPokemon class.
from collections import namedtupleAttack = namedtuple('Attack', ('name', 'damage'))class Pikachu(BasicPokemon):  def main_attack(self):    return Attack('Thunder Shock', 5)class Charmander(BasicPokemon):  def main_attack(self):    return Attack('Flame Thrower', 5)
Now we can create objects from these classes without problems.
Note that you can also create abstract properties using the same abstractmethod decorator.
from abc import (  ABC,  abstractmethod,)class BasicPokemon(ABC):  def __init__(self, name):    self.name = name  @property  @abstractmethod  def level(self):    ...  @abstractmethod  def main_attack(self):    ...
Now, we will need to implement the property level as well as the main_attack method in a child class.
class Pikachu(BasicPokemon):  @property  def level(self):    return 1  def main_attack(self):    return Attack('Thunder Shock', 5)
Some of you might be wondering why we couldn’t just use a normal class (i.e. not inherit from ABC) and raise a NotImplementerError for methods that have not been implemented, like in the example below.
class NotAbstractBasicPokemon:  def __init__(self, name):    self.name = name    self._level = 1  def main_attack(self):    raise NotImplementedError()
The code above will let you create objects from NotAbstractBasicPokemon and will only fail when you try to use main_attack. This is usually not desirable behaviour.
In contrast, trying to create an object from BasicPokemon results in an error immediately, as we saw earlier.
In the example we have been using in this post, BasicPokemon ‘s purpose was to be used as a blueprint for creating other classes for specific kinds of Pokemon. There are a few obvious benefits to having an abstract class that everyone needs to inherit from to create a new Pokemon:
Avoid code duplication.
Ensure consistency in how others implement the subclasses.
Ensure no one forgets to implement critical methods and properties in subclasses.
The above points are especially important when you are working in a team and you expect other people to re-use/extend the code.
In this post, we had a look at Python’s abstract classes and how they give us a way to describe how we expect subclasses to be implemented and warn users (i.e. other programmers or our future selves) when critical parts are not defined.
eminik355.medium.com
Follow me on Medium for updates.
Join Medium with my referral link for unlimited access to posts. Your membership fee directly supports me and the writers you read.
[1] https://docs.python.org/3/library/abc.html
[2] https://realpython.com/python-interface/
More from the same author.