RC: W5 D5 — When to use `NotImplemented` and `NotImplementedError`

March 15, 2024

Today I spent most of my day preparing a presentation on databases that I will give on Monday, so not much coding done. So I thought I would share a small thing I learned this week to distinguish “not implemented” cases.

In the Python source code chunk I shared yesterday, after executing the comparison operation, there is a check testing whether the result is Py_NotImplemented. This corresponds to cases where we return NotImplemented in Python. There is also a raise NotImplementedError that is sometimes used, and I was a bit confused as to when to use one or the other. Here is what I figured:

Here is a small snippet of code that should make the difference between the two clearer:

class MyParentClass:
    def my_method(self):
        raise NotImplementedError("Subclasses must implement this method.")


class MyDerivedClass(MyParentClass):
    def my_method(self):
        print("MyDerivedClass implementation of my_method.")


class MyComparisonClass:
    def __eq__(self, other):
        if not isinstance(other, MyComparisonClass):
            return NotImplemented
        # comparison logic here

What is interesting about explicitly declaring the return NotImplemented is that it allows to prevent unintended behaviors. For example, let’s suppose we have a class whose __eq__ method operates on the key attribute but does not explcitly state a NotImplemented case:

class MyClass1:
    def __init__(self, key):
        self.key = key

    def __eq__(self, other):
        return self.key == other.key


class MyClass2:
    def __init__(self, key, value):
        self.key = key
        self.value = value

    def __eq__(self, other):
        return self.key == other.key and self.value == other.value

With this, executing MyClass1(key="a") == MyClass2(key="a", value="a") would indicate that the two objects are equal (because they have the same key) without giving any warning. By adding the NotImplemented case when the second object is of a different instance, we are warned that the comparison being executed might not be what is intended – which is much better!