Python’s NotImplementedError for Documenting Inheritance

Adrienne Domingus
4 min readApr 24, 2020

Inheritance in Python, like in most other Object Oriented languages, allows us to re-use shared behavior, and allow us to abstract it away so that we don’t have to think about the implementation of certain behavior.

This is awesome! Developers spend a lot of time thinking about how to avoid repeating themselves. But as with everything in software, there are tradeoffs. Abstracting implementations away can mean that it’s not immediately obvious what behavior a class needs to define for itself, vs. what the parent class handles.

All of the examples in this post function when implemented properly, and will throw an error when they’re not. Which to follow in your code may be a matter of personal preference or following established patterns in a codebase, but let’s look at a few and talk through the benefits and downsides of each!

Let’s say, for example, we’re building a system that relies on user events, and we need to trigger certain types events at the relevant places in the application. We might build a base Event class that has behavior on how to generate its data and send it, from which specific event types can inherit. The base class might look something like this:

class Event():
def generate_data(self):
data = {
'user': {
'first_name': self.user.first_name
}
}
return data
def send(self):
...

Now, if we’re building banking software, we may need to track specific types of events, such as a deposit. This doesn’t have any custom behavior yet, but the barest implementations might look something like this:

class DepositEvent(Event):
def __init__(self, deposit):
self.deposit = deposit

Now, you might have noticed that these won’t actually work as is. The base Event class calls self.user in its generate_data method, but none of our classes implement that. However, the deposit object that our event is instantiated with has a reference to a user, so we can go ahead and add it:

class DepositEvent(Event):
def __init__(self, deposit):
self.deposit = deposit
self.user = deposit.user