Pourquoi renvoyer NotImplemented au lieu de déclencher NotImplementedError

283

Python a un singleton appelé NotImplemented.

Pourquoi voudrait-on jamais revenir NotImplementedau lieu de soulever l' NotImplementedErrorexception? Cela ne rendra-t-il pas simplement plus difficile la recherche de bogues, tels que le code qui exécute des méthodes invalides?

abyx
la source

Réponses:

281

C'est parce __lt__()que les méthodes de comparaison associées sont assez couramment utilisées indirectement dans les sortes de listes et autres. Parfois, l'algorithme choisira d'essayer d'une autre manière ou de choisir un gagnant par défaut. La levée d'une exception sortirait du cadre à moins d'être interceptée, alors qu'elle NotImplementedn'est pas levée et peut être utilisée dans d'autres tests.

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

Pour résumer ce lien:

" NotImplementedsignale au runtime qu'il doit demander à quelqu'un d'autre de satisfaire l'opération. Dans l'expression a == b, si a.__eq__(b)retourne NotImplemented, alors Python essaie b.__eq__(a). S'il en bsait assez pour retourner Trueou False, alors l'expression peut réussir. Si ce n'est pas le cas, alors le runtime retomber dans le comportement intégré (qui est basé sur l'identité pour ==et !=). "

Joint
la source
5
Je serais prudent en l'utilisant, comme le montre ce lien vers la fin du document.
Jason Coon,
16
Lorsque l'interpréteur Python vérifie s'il a a.__eq__(b)renvoyé NotImplemented, ne pourrait-il pas tout aussi facilement attraper NotImplementedError à la place (et appeler b.__eq__(a)ou autre chose alors)?
Veky
24
@Veky. La levée d'une exception a probablement un surcoût plus élevé. Toute surcharge dans une opération de tri sera agrandie par la taille de la liste, donc même si la différence était très petite, il serait toujours logique de trouver une implémentation plus rapide. Vous ne voulez pas non plus sortir de vos boucles et les ressaisir, ce qui nécessiterait une implémentation try / catch.
SpliFF
3
Pour le premier point, une solution encore plus rapide serait de synthétiser automatiquement lt en gt inversé, au lieu d'appeler toujours quelque chose qui retournera NotImplemented - et Python ne le fait pas. Je ne pense pas que la vitesse soit la raison ici. Et pour le second, je ne comprends pas ce que vous dites: le retour nécessitera autant de rupture de boucles que l'augmentation. En fait, vous pouvez imaginer return comme levant une exception Return spéciale, qui est toujours interceptée dans la portée d'appel.
Veky
2
>> "agrandi par la taille de la liste" Du moins, sauf si vous avez un tri O (n) que le monde devrait connaître.
Jonathan Hartley
107

Parce qu'ils ont des cas d'utilisation différents.

Citant les documents (Python 3.6):

Pas mis en œuvre

doivent être retournés par les méthodes spéciales binaires (par exemple __eq__(), __lt__(), __add__(), __rsub__(), etc.) pour indiquer que l'opération ne soit pas mis en œuvre par rapport à l'autre type

exception NotImplementedError

[...] Dans les classes de base définies par l'utilisateur, les méthodes abstraites devraient lever cette exception lorsqu'elles nécessitent que les classes dérivées remplacent la méthode, ou pendant le développement de la classe pour indiquer que l'implémentation réelle doit encore être ajoutée.

Voir les liens pour plus de détails.

Paolo
la source
Ce devrait être la réponse acceptée. C'est encore plus fort que les cas d'utilisation, c'est une indication d'intention - celui avec Erreur à la fin du nom signale qu'une Erreur s'est produite (parce que quelque chose n'est pas implémenté), l'autre n'est pas une erreur mais "propre" comportement. Je comparerais cela à la différence entre retourner NaN ou lever une ValueError.
Korone Il y a
13

L'une des raisons est la performance. Dans une situation comme les comparaisons riches, où vous pouvez effectuer de nombreuses opérations en peu de temps, la configuration et la gestion de nombreuses exceptions peuvent prendre beaucoup plus de temps que le simple retour d'une NotImplementedvaleur.

RichieHindle
la source