NotImplemented
NotImplemented constant for binary operations and method
delegation to enable proper operator fallback behavior.
NotImplemented is a built-in constant used for binary special
methods to show that a specific type does not have an implementation. The purpose
is to tell Python that "I'm not sure what to do with this other type—try the
other operand's reflected method or fall back".
Important: NotImplementedError is an exception
you raise to say that a method exists but has yet to be implemented. Don't
confuse the two!
Do not raise NotImplemented as an error:
class Storage:
def get(self, key):
raise NotImplemented # This is wrong!
Do not call NotImplemented—it's not callable:
>> NotImplemented()
TypeError: "NotImplementedType" object is not callable
Binary operators take two operands, with Python defining three different hooks:
- Left:
__add__,__sub__,__mul__,__truediv__ - Right/reflected:
__radd__,__rsub__,__rmul__,__rtruediv__ - In-place/augmented assignment:
__iadd__,__isub__,__imul__,__itruediv__
Python understands that NotImplemented is a special sentinel
and follows this strategy for a + b:
- Subclass check: If
type(b)is a strict subclass oftype(a)and defines__radd__, callb.__radd__(a)first - Left side: Call
a.__add__(b)first (if not handled above) - Check result: If it returns anything except
NotImplemented, we stop - Right side fallback: If it returns
NotImplemented, tryb.__radd__(a) - TypeError: If both sides return
NotImplemented, raiseTypeError
Here's an example of NotImplemented being used for binary operators:
class Left:
def __add__(self, other): # left side can't handle it
return NotImplemented
class Right:
def __radd__(self, other): # right side handles the reversed op
return "Right handled it"
print(Left() + Right()) # works because NotImplemented allowed the call for __radd__
For a more complex example showing the full fallback chain:
class L:
def __iadd__(self, other):
print("iadd")
return NotImplemented
def __add__(self, other):
print("add")
return NotImplemented
class R:
def __radd__(self, other):
print("radd")
return "handled"
x = L()
x += R() # prints: iadd -> add -> radd
NotImplemented also works with rich comparisons. These are
"rich" compared to the old single __cmp__ from Python 2:
class L:
def __lt__(self, other): # can't compare
return NotImplemented
class R:
def __gt__(self, other): # handles reversed "<"
return True
print(L() < R()) # True (via R.__gt__)
When both sides can handle the operation, the first successful one wins:
class A:
def __eq__(self, other):
return NotImplemented
class B:
def __eq__(self, other):
return True
print(A() == B()) # True (via B.__eq__)
Use NotImplemented when your class doesn't know how to handle
a specific operation with another type. This allows Python to try alternative
approaches rather than immediately failing, enabling more flexible and extensible
operator behavior.
Questions or feedback? Feel free to reach out!