Ausnahme auslösen vs. zurückgeben Keine in Funktionen?

85

Was ist eine bessere Vorgehensweise in einer benutzerdefinierten Funktion in Python: raiseeine Ausnahme oder return None? Zum Beispiel habe ich eine Funktion, die die neueste Datei in einem Ordner findet.

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

Eine andere Möglichkeit besteht darin, die Ausnahme zu belassen und sie im Anrufercode zu behandeln, aber ich denke, es ist klarer, mit a FileNotFoundErrorals mit a umzugehen IndexError. Oder ist es eine schlechte Form, eine Ausnahme mit einem anderen Namen erneut auszulösen?

Parcydarks
quelle
3
Ich neige dazu, eine Ausnahme auszulösen, sodass ich gezwungen bin, die Ausnahme in der aufrufenden Funktion zu behandeln. Wenn ich vergesse zu überprüfen, ob die Ausgabe in der aufrufenden Funktion None ist, könnte ein latenter Fehler auftreten. Wenn Sie None zurückgegeben haben, löst die nächste Zeile in der aufrufenden Funktion hoffentlich einen AttributeError aus. Wenn der zurückgegebene Wert jedoch zu einem Wörterbuch hinzugefügt wird und dann 100 Funktionsaufrufe in einer anderen Quelldatei einen AttributeError auslösen, haben Sie Spaß daran, herauszufinden, warum dieser Wert None war.
IceArdor
Im Allgemeinen vermeide ich auch Werte, die eine besondere Bedeutung haben oder mehrere Signaturen für eine Funktion haben (es könnte eine Zeichenfolge oder Keine zurückgeben).
IceArdor

Antworten:

91

Es ist wirklich eine Frage der Semantik. Was bedeutet foo = latestpdf(d) Mittelwert ?

Ist es völlig vernünftig, dass es keine neueste Datei gibt? Dann geben Sie einfach None zurück.

Erwarten Sie, immer eine aktuelle Datei zu finden? Eine Ausnahme auslösen. Und ja, es ist in Ordnung, eine angemessenere Ausnahme erneut zu erheben.

Wenn dies nur eine allgemeine Funktion ist, die für jedes Verzeichnis gelten soll, würde ich die erstere ausführen und None zurückgeben. Wenn das Verzeichnis beispielsweise ein bestimmtes Datenverzeichnis sein soll, das die bekannten Dateien einer Anwendung enthält, würde ich eine Ausnahme auslösen.

Eevee
quelle
3
Ein weiterer zu berücksichtigender Punkt: Wenn eine Ausnahme ausgelöst wird, kann eine Nachricht angehängt werden, dies können wir jedoch bei der Rückkehr nicht tun None.
Kawing-Chiu
9

Ich würde ein paar Vorschläge machen, bevor ich Ihre Frage beantworte, da sie die Frage für Sie beantworten kann.

  • Nennen Sie Ihre Funktionen immer beschreibend. latestpdfbedeutet für niemanden sehr wenig, aber wenn Sie sich Ihre Funktion latestpdf()ansehen, erhalten Sie das neueste PDF. Ich würde vorschlagen, dass Sie es nennen getLatestPdfFromFolder(folder).

Sobald ich dies tat, wurde klar, was es zurückgeben sollte. Wenn es kein PDF gibt, eine Ausnahme auslösen. Aber warte dort mehr ..

  • Halten Sie die Funktionen klar definiert. Da es nicht offensichtlich ist, was somefuc tun soll, und es (anscheinend) nicht offensichtlich ist, wie es sich auf das neueste PDF bezieht, würde ich vorschlagen, dass Sie es herausnehmen. Dies macht den Code viel lesbarer.

for folder in folders:
   try:
       latest = getLatestPdfFromFolder(folder)
       results = somefuc(latest)
   except IOError: pass

Hoffe das hilft!

rh0dium
quelle
Oder get_latest_pdf_from_folder. In der Tat, Pep8: "Funktionsnamen sollten in Kleinbuchstaben geschrieben sein, wobei die Wörter nach Bedarf durch Unterstriche getrennt sind, um die Lesbarkeit zu verbessern."
PatrickT
7

Normalerweise bevorzuge ich es, Ausnahmen intern zu behandeln (dh try / außer innerhalb der aufgerufenen Funktion, möglicherweise wird None zurückgegeben), da Python dynamisch typisiert ist. Im Allgemeinen halte ich es für einen Urteilsruf auf die eine oder andere Weise, aber in einer dynamisch typisierten Sprache gibt es kleine Faktoren, die den Ausschlag geben, die Ausnahme nicht an den Anrufer weiterzugeben:

  1. Jeder, der Ihre Funktion aufruft, wird nicht über die Ausnahmen informiert, die ausgelöst werden können. Es wird zu einer Art Kunstform, zu wissen, nach welcher Art von Ausnahme Sie suchen (und generische Ausnahmen außer Blöcken sollten vermieden werden).
  2. if val is Noneist etwas einfacher als except ComplicatedCustomExceptionThatHadToBeImportedFromSomeNameSpace. Im Ernst, ich hasse es, daran denken zu müssen, from django.core.exceptions import ObjectDoesNotExistoben in all meinen Django-Dateien zu tippen, um einen wirklich häufigen Anwendungsfall zu behandeln. Lassen Sie den Editor in einer statisch typisierten Welt dies für Sie tun.

Ehrlich gesagt ist es jedoch immer ein Urteilsspruch, und die von Ihnen beschriebene Situation, in der die aufgerufene Funktion einen Fehler empfängt, dem sie nicht helfen kann, ist ein hervorragender Grund, eine sinnvolle Ausnahme erneut auszulösen. Sie haben genau die richtige Idee, aber es sei denn, Ihre Ausnahme liefert aussagekräftigere Informationen in einem Stack-Trace als

AttributeError: 'NoneType' object has no attribute 'foo'

Neun von zehn Fällen wird der Anrufer sehen, wenn Sie ein unbehandeltes None zurückgeben. Machen Sie sich keine Sorgen.

(All diese causeDinge lassen mich wünschen, dass Python-Ausnahmen standardmäßig die Attribute haben, wie in Java, mit denen Sie Ausnahmen an neue Ausnahmen übergeben können, damit Sie alles neu werfen können, was Sie wollen, und niemals die ursprüngliche Ursache des Problems verlieren.)

David Berger
quelle
Das Argument, dass die möglichen Ausnahmen nicht definiert sind und daher schwer zu fassen sind, ist ein sehr gültiges Argument für Python.
Snorberhuis
4

mit der Eingabe von Python 3.5 :

Beispielfunktion bei Rückgabe Keine ist:

def latestpdf(folder: str) -> Union[str, None]

und wenn eine Ausnahme ausgelöst wird, gilt Folgendes:

def latestpdf(folder: str) -> str 

Option 2 scheint lesbarer und pythonischer zu sein

(+ Option zum Hinzufügen eines Kommentars zur Ausnahme wie oben angegeben.)

Asaf
quelle
4
Union[str, None]sollte seinOptional[str]
Georgy
2
eine Abkürzung, aber Sie haben Recht, es ist besser lesbar. nicht bearbeitet, daher sind beide Optionen hier.
Asaf
2 ist möglicherweise besser lesbar, aber (leider?) Typhinweise weisen nicht darauf hin, dass eine Ausnahme ausgelöst werden könnte. Ich habe kürzlich festgestellt, dass 1 dazu beiträgt, mehr Fehler zu erkennen, da Sie gezwungen sind, eine None-Rückgabe zu verarbeiten.
Jonespm
2

Im Allgemeinen würde ich sagen, dass eine Ausnahme ausgelöst werden sollte, wenn etwas Katastrophales aufgetreten ist, das nicht wiederhergestellt werden kann (dh Ihre Funktion befasst sich mit einer Internetressource, mit der keine Verbindung hergestellt werden kann), und Sie sollten None zurückgeben, wenn Ihre Funktion wirklich etwas zurückgeben sollte Es wäre jedoch nicht angebracht, etwas zurückzugeben (dh "Keine", wenn Ihre Funktion beispielsweise versucht, eine Teilzeichenfolge in einer Zeichenfolge abzugleichen).


quelle