Python NotImplemented constant
Question:
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.
Answers:
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.
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.
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__
.
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.
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.
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.
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__
.