417

Python has a singleton called NotImplemented.

Why would someone want to ever return NotImplemented instead of raising the NotImplementedError exception? Won't it just make it harder to find bugs, such as code that executes invalid methods?

Paolo
  • 20,112
  • 21
  • 72
  • 113
abyx
  • 69,862
  • 18
  • 95
  • 117

5 Answers5

389

It's because __lt__() and related comparison methods are quite commonly used indirectly in list sorts and such. Sometimes the algorithm will choose to try another way or pick a default winner. Raising an exception would break out of the sort unless caught, whereas NotImplemented doesn't get raised and can be used in further tests.

http://jcalderone.livejournal.com/32837.html

To summarise that link:

"NotImplemented signals to the runtime that it should ask someone else to satisfy the operation. In the expression a == b, if a.__eq__(b) returns NotImplemented, then Python tries b.__eq__(a). If b knows enough to return True or False, then the expression can succeed. If it doesn't, then the runtime will fall back to the built-in behavior (which is based on identity for == and !=)."

user
  • 5,370
  • 8
  • 47
  • 75
SpliFF
  • 38,186
  • 16
  • 91
  • 120
  • 5
    I would be careful using it, as this link points out near the end of the document. – Jason Coon May 18 '09 at 18:04
  • 25
    When Python interpreter checks whether `a.__eq__(b)` returned NotImplemented, couldn't it just as easily catch NotImplementedError instead (and call `b.__eq__(a)` or whatever then)? – Veky Jul 25 '13 at 18:05
  • 36
    @Veky. Raising an exception probably has a higher overhead. Any overhead in a sort operation will get magnified by the size of the list so even if the difference was very small it would still make sense to find a faster implementation. You also don't want to be breaking out of your loops and reentering them which a try/catch implementation would require. – SpliFF Jul 27 '13 at 06:05
  • 3
    For the first point, even faster solution would be to automatically synthesize lt as reversed gt, instead of always calling something that will return NotImplemented - and Python doesn't do that. I don't think speed is the reason here. And for the second, I don't understand what you're saying: return will require just as much breaking out of loops as raise would. In fact, you can imagine return as raising a special Return exception, which is always caught in the calling scope. – Veky Jul 30 '13 at 06:03
  • 2
    >> "magnified by the size of the list" At least, unless you have an O(n) sort the world ought to know about. – Jonathan Hartley Feb 06 '14 at 12:37
  • 1
    @JonathanHartley Counting sort is O(n), for your information. – Sapphire_Brick Jun 26 '20 at 15:51
  • @JonathanHartley I take that back; it's actually O(n + k). See https://en.wikipedia.org/wiki/Counting_sort. – Sapphire_Brick Jun 26 '20 at 17:09
278

Because they have different use cases.

Quoting the docs (Python 3.6):

NotImplemented

should be returned by the binary special methods (e.g. __eq__(), __lt__(), __add__(), __rsub__(), etc.) to indicate that the operation is not implemented with respect to the other type

exception NotImplementedError

[...] In user defined base classes, abstract methods should raise this exception when they require derived classes to override the method, or while the class is being developed to indicate that the real implementation still needs to be added.

See the links for details.

Paolo
  • 20,112
  • 21
  • 72
  • 113
  • 34
    This should be the accepted answer. It's even stronger than use cases, it is an indication of intent - the one with Error on the end of the name signals that an Error has occurred (because something isn't implemented), the other one is not an error but "proper" behavior. I would liken it to the difference between returning NaN or raising a ValueError. – Corvus Jun 23 '20 at 08:55
  • @Corvus this comment should be the accepted answer, and the answer itself isn't the generic case whereas the comment is. – Gulzar Jun 28 '23 at 08:49
20

One reason is performance. In a situation like rich comparisons, where you could be doing lots of operations in a short time, setting up and handling lots of exceptions could take a lot longer than simply returning a NotImplemented value.

RichieHindle
  • 272,464
  • 47
  • 358
  • 399
17

Returning NotImplemented by a function is something like declaring that the function is unable to process the inputs but instead of raising exception, the control is transferred to another function known as Reflection Function with a hope that the Reflection Function might be able to process the inputs.

Firstly, the Reflection Functions associated with any functions are predefined. Secondly when the original function returns NotImplemented, interpreter runs the Reflection Function but on the flipped order of input arguments.

You can find detailed examples here

ahsan naeem
  • 245
  • 2
  • 9
  • Note that this answer is wrong: (1) this only applies to special functions like __eq__, __add__ etc..., your regular functions will return NotImplemented singleton as usual and (2) there is no such thing as "reflection function", this is something random bloggers made up. Official docs ( https://docs.python.org/3/library/constants.html#NotImplemented ) say "interpreter will try the reflected operation on the other type" - so if x.__add__(y) returns NotImplemented, python will call y._add__(x) – theamk Aug 11 '23 at 02:33
0

If a special method supporting a binary operation is not implemented it should return NotImplemented. On the other hand, NotImplementedError should be raised from abstract methods inside user defined base classes to indicate that derived classes should override those methods.

Basically, NotImplemented helps the interpreter support a binary operation, whereas the NotImplementedError throws an exception. You can find a general usage here.

Ataf
  • 39
  • 3