Why do we need the abstract base class library when we have NotImplementedError?
Here's a typical use of abstract base classes in python:
from abc import ABC
class MyBaseClass(ABC):
@abstractmethod
def __init__(self):
print("Here's some logic that I want all subclasses to have")
class ChildClass(MyBaseClass):
def __init__(self):
super().__init__()
print("Here's some additional logic that I want this subclass to have")
The behavior is: it's not possible to instantiate MyBaseClass
, but it is possible to subclass it, and MyBaseClass
includes logic inherited by all subclasses.
Here's another way to get this same behavior:
class MyBaseClass:
def __init__(self):
if type(self) == MyBaseClass:
raise NotImplementedError
print("Here's some logic that I want all subclasses to have")
class ChildClass(MyBaseClass):
def __init__(self):
super().__init__()
print("Here's some additional logic that I want this subclass to have")
As far as I can tell, this approach has the exact same behavior, has the same amount of code, is equally explicit, and uses only native Python. So what does ABC
do that this approach can't handle?
Abstract base classes prevent you from instantiating an object unless you implement the abstract methods. This raises the error immediately and alerts you to the fact that you need to implement the method. This is especially helpful if you instantiate the class but don't call the method until after a lengthy execution process: you won't raise NotImplementedError
until you call the method.
import abc
class AbstractClass(abc.ABC):
@abc.abstractmethod
def test(self):
pass
class NotAbstractClass:
def test(self):
raise NotImplementedError()
class AbstractInheritor(AbstractClass):
pass
class NotAbstractInheritor(NotAbstractClass):
pass
x = AbstractInheritor() # raises error immediately
y = NotAbstractInheritor() # no error despite not implementing test
In fact, the line where I create x
is caught by my type checker, so I do not even need to run the code to know there is a problem. Meanwhile, y
is perfectly fine.