35

Looking through decimal.py, it uses NotImplemented in many special methods. e.g.

class A(object):
    def __lt__(self, a):
        return NotImplemented

    def __add__(self, a):
        return NotImplemented

The Python docs say:

NotImplemented

Special value which can be returned by the “rich comparison” special methods (__eq__(), __lt__(), and friends), to indicate that the comparison is not implemented with respect to the other type.

It doesn't talk about other special methods and neither does it describe the behavior.

It seems to be a magic object which if returned from other special methods raises TypeError, and in “rich comparison” special methods does nothing.

e.g.

print A() < A()

prints True, but

print A() + 1

raises TypeError, so I am curious as to what's going on and what is the usage/behavior of NotImplemented.

deadly
  • 1,194
  • 14
  • 24
Anurag Uniyal
  • 85,954
  • 40
  • 175
  • 219
  • 2
    All Correct. You have completely describe NotImplemented. What's the question? – S.Lott Jun 30 '09 at 10:06
  • my question was that if in doc, it specially mentions “rich comparison” special methods, other methods should ignore it, after all it is just another object, i could not find doc explaining general behavior or how to handle NotImplemented – Anurag Uniyal Jun 30 '09 at 11:56

3 Answers3

36

NotImplemented allows you to indicate that a comparison between the two given operands has not been implemented (rather than indicating that the comparison is valid, but yields False, for the two operands).

From the Python Language Reference:

For objects x and y, first x.__op__(y) is tried. If this is not implemented or returns NotImplemented, y.__rop__(x) is tried. If this is also not implemented or returns NotImplemented, a TypeError exception is raised. But see the following exception:

Exception to the previous item: if the left operand is an instance of a built-in type or a new-style class, and the right operand is an instance of a proper subclass of that type or class and overrides the base's __rop__() method, the right operand's __rop__() method is tried before the left operand's __op__() method. This is done so that a subclass can completely override binary operators. Otherwise, the left operand's __op__() method would always accept the right operand: when an instance of a given class is expected, an instance of a subclass of that class is always acceptable.

Brandon E Taylor
  • 24,881
  • 6
  • 47
  • 71
  • 5
    ok, so in general behavior is if return value is NotImplemented try something else, if that is not possible raise TypeError – Anurag Uniyal Jun 30 '09 at 07:15
  • 2
    Is it a bad idea to use it in other contexts, like, say, a dictionary mapping between different types? – endolith Apr 23 '14 at 14:39
  • 1
    The link is broken, could you update the link? I want to see the source page. – Rick Jan 18 '22 at 08:28
7

It actually has the same meaning when returned from __add__ as from __lt__, the difference is Python 2.x is trying other ways of comparing the objects before giving up. Python 3.x does raise a TypeError. In fact, Python can try other things for __add__ as well, look at __radd__ and (though I'm fuzzy on it) __coerce__.

# 2.6
>>> class A(object):
...   def __lt__(self, other):
...     return NotImplemented
>>> A() < A()
True

# 3.1
>>> class A(object):
...   def __lt__(self, other):
...     return NotImplemented
>>> A() < A()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: A() < A()

See Ordering Comparisions (3.0 docs) for more info.

0

If you return it from __add__ it will behave like the object has no __add__ method, and raise a TypeError.

If you return NotImplemented from a rich comparison function, Python will behave like the method wasn't implemented, that is, it will defer to using __cmp__.

SilentGhost
  • 307,395
  • 66
  • 306
  • 293
Lennart Regebro
  • 167,292
  • 41
  • 224
  • 251