Dan Imhoff

Developer and Photographer

02 June 2015
Usages of NotImplemented and NotImplementedError in Python

NotImplemented is a built-in constant that has been in Python for a very long time. Its primary purpose is to be a return value for rich comparison methods such as __eq__() and __lt__(), which indicates the type on which these methods exist cannot be compared in that manner, e.g.:

class Foo:
    def __lt__(self, obj):
        return NotImplemented

print(Foo() < Foo())

Which yields:

Traceback (most recent call last):
  File "test.py", line 5, in <module>
    print(Foo() < Foo())
TypeError: unorderable types: Foo() < Foo()

Python expects NotImplemented to be returned from these comparison methods, instead of expecting NotImplementedError to be raised from them, due to performance concerns; it is much more performant to check the return value of a method rather than to catch an exception from a method. (This does not mean I advocate C-style error checking for Python. Exceptions are your friend, but when overused they can lead to minute performance issues.)

Most Python developers are familiar with the NotImplementedError exception. It is common to see this in Python codebases, where subclasses of BaseClass must implement foo(), or face a runtime exception:

class BaseClass:
    def foo(self):
        raise NotImplementedError


class GoodChildClass(BaseClass):
    def foo(self):
        return 'bar'


class BadChildClass(BaseClass):
    pass


good = GoodChildClass()
print(good.foo())
bad = BadChildClass()
print(bad.foo())

As expected, bar is printed (from the implemented foo() method in GoodChildClass), and then a NotImplementedError is raised because BadChildClass failed to implement foo():

bar
Traceback (most recent call last):
  File "test.py", line 18, in <module>
    print(bad.foo())
  File "test.py", line 3, in foo
    raise NotImplementedError
NotImplementedError

A similar approach can be used for properties, too. Just raise NotImplementedError in the getter and setter (and deleter, optionally) of the base class, and your child classes are forced to comply to your interface.

What about class properties, though? How do we get NotImplementedError to be raised when an implementation detail is missing? We may try:

class BaseClass:
    _foo = None

    @property
    def foo(self):
        if self._foo is None:
            raise NotImplementedError
        return self._foo


base = BaseClass()

But there are a couple of problems.

  1. Once a property is used, foo can no longer be used at class-level. That is, BaseClass.foo returns the property object, while base.foo raises the NotImplementedError.
  2. It is not immediately clear what must be implemented in child classes.

Let’s try again, using a classproperty and NotImplemented.

class classproperty:
    def __init__(self, f):
        self.f = f

    def __get__(self, obj, owner):
        return self.f(owner)


class BaseClass:
    _foo = NotImplemented

    @classproperty
    def foo(cls):
        if cls._foo is NotImplemented:
            raise NotImplementedError
        return cls._foo


base = BaseClass()

Our two problems are solved. Accessing BaseClass.foo raises a NotImplementedError and we can immediately see which class properties our child classes need to implement–they have a value of NotImplemented, which screams IMPLEMENT ME.

NotImplemented can also be used for kwarg defaults instead of None to immediately signify a required kwarg:

class Foo:
    def __init__(self, bar, session=NotImplemented, baz=None):
        self.bar = bar
        if session is NotImplemented:
            raise ValueError("session is required")
        self.session = session
        self.baz = baz


foo = Foo('bar')

We get:

Traceback (most recent call last):
  File "test.py", line 9, in <module>
    foo = Foo('bar)
  File "test.py", line 5, in __init__
    raise ValueError("session is required")
ValueError: session is required

The API of the Foo class and the error message are both clear as to what we need to do to make the program happy.

What other usages have you found for NotImplemented and NotImplementedError? Let me know!


— Dan Imhoff, 2015