Quelle est la meilleure pratique dans une fonction définie par l'utilisateur en Python: raise
une exception ou return None
? Par exemple, j'ai une fonction qui trouve le fichier le plus récent dans un dossier.
def latestpdf(folder):
# list the files and sort them
try:
latest = files[-1]
except IndexError:
# Folder is empty.
return None # One possibility
raise FileNotFoundError() # Alternative
else:
return somefunc(latest) # In my case, somefunc parses the filename
Une autre option est de laisser l'exception et de la gérer dans le code de l'appelant, mais je suppose qu'il est plus facile de traiter un FileNotFoundError
qu'un IndexError
. Ou est-ce une mauvaise forme de relancer une exception avec un nom différent?
C'est vraiment une question de sémantique. Qu'est-ce que foo = latestpdf(d)
signifie ?
Est-il parfaitement raisonnable de ne pas avoir le dernier fichier? Alors bien sûr, retournez None.
Vous attendez-vous toujours à trouver le dernier fichier? Lève une exception. Et oui, relancer une exception plus appropriée est acceptable.
S'il s'agit simplement d'une fonction générale supposée s'appliquer à n'importe quel répertoire, je referais la première et renverrais None. Si le répertoire est, par exemple, censé être un répertoire de données spécifique contenant le jeu de fichiers connu d'une application, je déclencherais une exception.
Je voudrais faire quelques suggestions avant de répondre à votre question car cela pourrait répondre à la question pour vous.
latestpdf
signifie très peu pour quiconque mais examiner votre fonction latestpdf()
obtient le dernier fichier PDF. Je suggérerais que vous le nommiez getLatestPdfFromFolder(folder)
.Dès que j'ai fait cela, il est devenu clair ce qu'il devrait retourner. S'il n'y a pas de pdf, une exception est levée. Mais attendez là plus ..
for folder in folders:
try:
latest = getLatestPdfFromFolder(folder)
results = somefuc(latest)
except IOError: pass
J'espère que cela t'aides!
Je préfère généralement gérer les exceptions en interne (c’est-à-dire essayer/sauf dans la fonction appelée, en renvoyant éventuellement la valeur None) car python est typé de manière dynamique. En général, j’estime qu’il s’agit d’un jugement, d’une manière ou d’une autre, mais dans un langage typé de manière dynamique, il existe de petits facteurs qui font pencher la balance en faveur du refus de transmettre l’exception à l’appelant:
if val is None
est un peu plus facile que except ComplicatedCustomExceptionThatHadToBeImportedFromSomeNameSpace
. Sérieusement, je déteste avoir à oublier de taper from Django.core.exceptions import ObjectDoesNotExist
en haut de tous mes fichiers Django uniquement pour gérer un cas d'utilisation très courant. Dans un monde statiquement typé, laissez l'éditeur le faire pour vous.Honnêtement, cependant, c'est toujours un jugement, et la situation que vous décrivez, où la fonction appelée reçoit une erreur qu'il ne peut pas aider, est une excellente raison de relancer une exception significative. Vous avez exactement la bonne idée, mais à moins que vous ne soyez une exception, vous obtiendrez des informations plus utiles dans une trace de pile.
AttributeError: 'NoneType' object has no attribute 'foo'
ce que l’appelant verra, neuf fois sur dix, si vous renvoyez un None non pris en charge, ne vous embêtez pas.
(Tout cela me fait souhaiter que les exceptions python aient les attributs cause
par défaut, comme en Java, ce qui vous permet de passer des exceptions à de nouvelles exceptions afin que vous puissiez rediffuser tout ce que vous voulez et ne jamais perdre la source d'origine du problème.)
En général, je dirais qu’une exception devrait être levée en cas de catastrophe irrécupérable (c’est-à-dire que votre fonction traite certaines ressources Internet qui ne peuvent pas être connectées) et vous devez renvoyer None si votre fonction doit réellement renvoyer quelque chose. mais rien ne conviendrait de renvoyer (c'est-à-dire "None" si votre fonction tente de faire correspondre une chaîne dans une chaîne, par exemple).
avec python 3.5's en tapant :
exemple de fonction lors du retour Aucun sera:
def latestpdf(folder: str) -> Union[str, None]
et quand levant une exception sera:
def latestpdf(folder: str) -> str
l'option 2 semble plus lisible et Pythonic
(+ option pour ajouter un commentaire à l'exception comme indiqué précédemment.)