Sollte ich in "Ausnahmen" -Anweisungen immer einen Ausnahmetyp angeben?
93
Bei Verwendung der PyCharm-IDE except:löst die Verwendung von ohne Ausnahmetyp eine Erinnerung von der IDE aus, dass diese Ausnahmeklausel ist Too broad.
Sollte ich diesen Rat ignorieren? Oder ist es pythonisch, immer den Ausnahmetyp anzugeben?
Wenn Sie mehr darüber als die Antworten erfahren möchten, schlucken Sie bei Google Ausnahmen. Sie können alle möglichen anderen interessanten Dinge neben ihnen tun. Code riecht ist ein anderer.
Tony Hopkinson
Antworten:
89
Es ist fast immer besser, einen expliziten Ausnahmetyp anzugeben. Wenn Sie eine nackte except:Klausel verwenden, werden möglicherweise andere Ausnahmen als die erwarteten abgefangen. Dies kann Fehler verbergen oder das Debuggen von Programmen erschweren, wenn sie nicht das tun, was Sie erwarten.
Wenn Sie beispielsweise eine Zeile in eine Datenbank einfügen, möchten Sie möglicherweise eine Ausnahme abfangen, die angibt, dass die Zeile bereits vorhanden ist, damit Sie eine Aktualisierung durchführen können.
Wenn Sie ein Bare angeben except:, wird auch ein Socket-Fehler angezeigt, der darauf hinweist, dass der Datenbankserver umgefallen ist. Es ist am besten, nur Ausnahmen abzufangen, mit denen Sie umgehen können. Oft ist es besser, wenn das Programm zum Zeitpunkt der Ausnahme fehlschlägt, als fortzufahren, sich aber auf seltsame, unerwartete Weise zu verhalten.
Ein Fall, in dem Sie möglicherweise ein Bare verwenden möchten, except:befindet sich auf der obersten Ebene eines Programms, das Sie immer ausführen müssen, z. B. eines Netzwerkservers. Aber dann müssen Sie sehr vorsichtig sein, um die Ausnahmen zu protokollieren, sonst ist es unmöglich herauszufinden, was falsch läuft. Grundsätzlich sollte es in einem Programm höchstens einen Ort geben, der dies tut.
Eine logische Folge all dessen ist , dass Ihr Code sollte nie tun , raise Exception('some message')weil es Client - Code zu verwenden zwingt except:(oder except Exception:das ist fast so schlimm). Sie sollten eine Ausnahme definieren, die für das Problem spezifisch ist, das Sie signalisieren möchten (möglicherweise von einer integrierten Ausnahmeunterklasse wie ValueErroroder erben TypeError). Oder Sie sollten eine bestimmte integrierte Ausnahme auslösen. Auf diese Weise können Benutzer Ihres Codes vorsichtig sein, wenn sie nur die Ausnahmen abfangen, die sie behandeln möchten.
+1 Sehr wahr. Noch mehr Spaß mit dem Beispiel: except:fängt auch (unter anderem) NameErrorund AttributeErrorwenn Sie also etwas im tryBlock falsch schreiben (z. B. wird Ihre "Einfügen" -Funktion tatsächlich aufgerufen, insert_oneweil jemand die Konsistenz nicht so sehr schätzte, wie er sollte), es versucht immer still zu update().
1
Was ist also, wenn Sie sicherstellen müssen, dass eine Ausnahme nicht über die aktuelle Anrufseite geworfen wird? Dh - Ich habe alle spezifischen Ausnahmen abgefangen, von denen ich erwarten kann, dass sie ausgelöst werden. Jetzt muss ich hinzufügen, dass "wenn dieses Ding, an das ich nicht gedacht habe, ausgelöst wird, muss ich es protokollieren, bevor es den laufenden Ausführungskontext beendet" (z main())?
Adam Parkin
Das ist die Art von Situation, über die ich in dem Absatz spreche, der mit "Ein Fall ..." beginnt. Es wird definitiv manchmal benötigt, aber jedes Programm sollte wirklich nur einen Ort haben, der das tut. Und Sie müssen darauf achten, dass im Protokoll deutlich wird, was passiert. Andernfalls haben Sie eine frustrierende Zeit, um herauszufinden, warum Ihr Programm das Ereignis / die Anforderung / was auch immer nicht richtig behandelt.
Babbageclunk
3
@ Delnan: Schlimmer als das. except Exception:wird fangen NameErrorund AttributeErrorauch. Was Bare except:so schlecht macht , ist, dass es Dinge fängt, die kein Geschäft haben, z. B. SystemExit(ausgelöst, wenn Sie anrufen exitoder sys.exit, und jetzt haben Sie einen beabsichtigten Exit verhindert) und KeyboardInterrupt(wieder, wenn der Benutzer trifft Ctrl-C, möchten Sie wahrscheinlich nicht weiterlaufen, nur um sie zu ärgern). Nur letzteres macht wirklich Sinn zu fangen, und es sollte explizit gefangen werden. Zumindest except Exception:lassen sich diese beiden normal ausbreiten.
ShadowRanger
38
Sie sollten den Rat des Dolmetschers nicht ignorieren.
Eine bloße Ausnahme: -Klausel fängt SystemExit- und KeyboardInterrupt-Ausnahmen ab, was es schwieriger macht, ein Programm mit Control-C zu unterbrechen, und kann andere Probleme verschleiern. Wenn Sie alle Ausnahmen abfangen möchten, die Programmfehler signalisieren, verwenden Sie außer Ausnahme: (nackte Ausnahme entspricht Ausnahme BaseException :).
Eine gute Faustregel besteht darin, die Verwendung von bloßen Ausnahmeklauseln auf zwei Fälle zu beschränken:
Wenn der Ausnahmebehandler den Traceback ausdrucken oder protokollieren wird; Zumindest wird dem Benutzer bekannt sein, dass ein Fehler aufgetreten ist. Wenn der Code einige Bereinigungsarbeiten ausführen muss, die Ausnahme dann jedoch mit Raise nach oben übertragen wird. versuchen Sie ... endlich kann ein besserer Weg sein, um diesen Fall zu behandeln.
Der springende Punkt bei Ausnahmen ist, das Problem so nahe wie möglich an der Stelle zu behandeln, an der es verursacht wurde.
Sie behalten also den Code, der unter außergewöhnlichen Umständen das Problem und die Auflösung "nebeneinander" auslösen könnte.
Die Sache ist, dass Sie nicht alle Ausnahmen kennen können, die von einem Code ausgelöst werden könnten. Alles, was Sie wissen können, ist, dass Sie, wenn es sich um eine Ausnahme handelt, bei der eine Datei nicht gefunden wurde, diese abfangen und den Benutzer auffordern können, eine Ausnahme zu erhalten, die die Funktion ausführt oder abbricht.
Wenn Sie versuchen, das zu umgehen, wird unabhängig davon, welches Problem in Ihrer Dateiroutine aufgetreten ist (schreibgeschützt, Berechtigungen, Benutzerkontensteuerung, nicht wirklich ein PDF usw.), jeder in Ihre Datei gefunden, der nicht gefunden wurde, und Ihr Benutzer schreit "aber es ist da, dieser Code ist Mist"
Jetzt gibt es einige Situationen, in denen Sie vielleicht alles fangen, aber sie sollten bewusst ausgewählt werden.
Sie werden abgefangen, machen eine lokale Aktion rückgängig (z. B. das Erstellen oder Sperren einer Ressource (Öffnen einer Datei zum Schreiben zum Schreiben), dann lösen Sie die Ausnahme erneut aus, um sie auf einer höheren Ebene zu behandeln).
Dem anderen ist es egal, warum es schief gelaufen ist. Zum Beispiel drucken. Möglicherweise haben Sie einen Haken, der besagt, dass es ein Problem mit Ihrem Drucker gibt. Bitte klären Sie es und beenden Sie die Anwendung nicht deswegen. Ähnlich vergeblich, wenn Ihr Code eine Reihe separater Aufgaben nach einem Zeitplan ausführt, möchten Sie nicht, dass das Ganze stirbt, da eine der Aufgaben fehlgeschlagen ist.
Hinweis Wenn Sie die oben genannten Schritte ausführen, kann ich keine Ausnahmeprotokollierung empfehlen, z. B. versuchen Sie, das Ende des Fangprotokolls hoch genug zu ermitteln.
Ich würde sagen, es geht um Balance. Sie müssen die Ausnahme früh genug abfangen, um sich davon erholen zu können, und spät genug, um zu wissen, wohin Sie damit gehen sollen. Aus diesem Grund verursacht die Behandlung von Java-Ausnahmen solche Probleme, da Sie die Ausnahme bei jedem Schritt neu verpacken müssen und Informationen verlieren.
Dhill
2
+1 für "Es ist dir egal, warum es schief gelaufen ist." Ich verwende es an mehreren Stellen um eine einzelne Codezeile herum, wo ich ein Datum / eine Uhrzeit von einer URL analysiere. Die Datums- / Zeit-Analysebibliothek von Drittanbietern listet nicht alle Ausnahmen auf, die sie auslösen kann (ich habe OverflowError und TypeError zusätzlich zum Standard ValueError gefunden, aber es gibt wahrscheinlich noch mehr), und es ist mir auch wirklich egal, warum Es wurde eine Ausnahme ausgelöst. Ich möchte dem Benutzer nur eine angemessene Fehlermeldung zurückgeben, die besagt, dass mit Datum und Uhrzeit etwas nicht stimmt.
Michael Rodby
3
Sie werden damit auch zB Control-C fangen, also tun Sie es nicht, es sei denn, Sie "werfen" es erneut. In diesem Fall sollten Sie jedoch lieber "finally" verwenden.
Würde die Verwendung except Exception:die oben genannten Typen vermeiden, die wir nicht fangen wollen?
HorseloverFat
except Exceptionist gut.
Ulrich Eckhardt
4
@HorseloverFat: except Exceptionfängt SyntaxErrorund MemoryErrorweil es ihre Basisklasse ist. KeyboardInterrupt, SystemExit( sys.exit()ausgelöst von ) werden nicht abgefangen (es handelt sich um unmittelbare BaseException-Unterklassen)
jfs
klingt dann nicht ideal - besser genauer spezifizieren.
HorseloverFat
3
Hier sind die Orte, an denen ich außer ohne Typ benutze
schnelles und schmutziges Prototyping
Das ist die Hauptverwendung in meinem Code für ungeprüfte Ausnahmen
main () -Funktion der obersten Ebene, bei der ich jede nicht erfasste Ausnahme protokolliere
Ich füge dies immer hinzu, damit der Produktionscode keine Stapelspuren verschüttet
zwischen Anwendungsschichten
Ich habe zwei Möglichkeiten:
Erster Weg, dies zu tun: Wenn eine Ebene höherer Ebene eine Funktion niedrigerer Ebene aufruft, werden die Aufrufe in typisierte Ausnahmen eingeschlossen, um die "obersten" Ausnahmen niedrigerer Ebene zu behandeln. Aber ich füge eine generische Ausnahme-Anweisung hinzu, um nicht behandelte Ausnahmen der unteren Ebene in den Funktionen der unteren Ebene zu erkennen.
Ich bevorzuge es so, ich finde es einfacher zu erkennen, welche Ausnahmen angemessen abgefangen werden sollten: Ich "sehe" das Problem besser, wenn eine Ausnahme niedrigerer Ebene von einer höheren Ebene protokolliert wird
Zweiter Weg, dies zu tun: Bei allen Funktionen der obersten Ebene von Ebenen der unteren Ebene wird der Code in eine generische Ausnahme eingeschlossen, damit alle nicht behandelten Ausnahmen auf dieser bestimmten Ebene abgefangen werden.
Einige Mitarbeiter bevorzugen diesen Weg, da Ausnahmen auf niedrigerer Ebene in Funktionen auf niedrigerer Ebene beibehalten werden, zu denen sie "gehören".
Antworten:
Es ist fast immer besser, einen expliziten Ausnahmetyp anzugeben. Wenn Sie eine nackte
except:
Klausel verwenden, werden möglicherweise andere Ausnahmen als die erwarteten abgefangen. Dies kann Fehler verbergen oder das Debuggen von Programmen erschweren, wenn sie nicht das tun, was Sie erwarten.Wenn Sie beispielsweise eine Zeile in eine Datenbank einfügen, möchten Sie möglicherweise eine Ausnahme abfangen, die angibt, dass die Zeile bereits vorhanden ist, damit Sie eine Aktualisierung durchführen können.
Wenn Sie ein Bare angeben
except:
, wird auch ein Socket-Fehler angezeigt, der darauf hinweist, dass der Datenbankserver umgefallen ist. Es ist am besten, nur Ausnahmen abzufangen, mit denen Sie umgehen können. Oft ist es besser, wenn das Programm zum Zeitpunkt der Ausnahme fehlschlägt, als fortzufahren, sich aber auf seltsame, unerwartete Weise zu verhalten.Ein Fall, in dem Sie möglicherweise ein Bare verwenden möchten,
except:
befindet sich auf der obersten Ebene eines Programms, das Sie immer ausführen müssen, z. B. eines Netzwerkservers. Aber dann müssen Sie sehr vorsichtig sein, um die Ausnahmen zu protokollieren, sonst ist es unmöglich herauszufinden, was falsch läuft. Grundsätzlich sollte es in einem Programm höchstens einen Ort geben, der dies tut.Eine logische Folge all dessen ist , dass Ihr Code sollte nie tun ,
raise Exception('some message')
weil es Client - Code zu verwenden zwingtexcept:
(oderexcept Exception:
das ist fast so schlimm). Sie sollten eine Ausnahme definieren, die für das Problem spezifisch ist, das Sie signalisieren möchten (möglicherweise von einer integrierten Ausnahmeunterklasse wieValueError
oder erbenTypeError
). Oder Sie sollten eine bestimmte integrierte Ausnahme auslösen. Auf diese Weise können Benutzer Ihres Codes vorsichtig sein, wenn sie nur die Ausnahmen abfangen, die sie behandeln möchten.quelle
except:
fängt auch (unter anderem)NameError
undAttributeError
wenn Sie also etwas imtry
Block falsch schreiben (z. B. wird Ihre "Einfügen" -Funktion tatsächlich aufgerufen,insert_one
weil jemand die Konsistenz nicht so sehr schätzte, wie er sollte), es versucht immer still zuupdate()
.main()
)?except Exception:
wird fangenNameError
undAttributeError
auch. Was Bareexcept:
so schlecht macht , ist, dass es Dinge fängt, die kein Geschäft haben, z. B.SystemExit
(ausgelöst, wenn Sie anrufenexit
odersys.exit
, und jetzt haben Sie einen beabsichtigten Exit verhindert) undKeyboardInterrupt
(wieder, wenn der Benutzer trifftCtrl-C
, möchten Sie wahrscheinlich nicht weiterlaufen, nur um sie zu ärgern). Nur letzteres macht wirklich Sinn zu fangen, und es sollte explizit gefangen werden. Zumindestexcept Exception:
lassen sich diese beiden normal ausbreiten.Sie sollten den Rat des Dolmetschers nicht ignorieren.
Aus dem PEP-8 Style Guide für Python:
quelle
except BaseException
bedeutet nicht dasselbe wieexcept
: in Python 2; In Python 2 können Sie Klassen alten Stils als Ausnahmen auslösen, von denen nicht geerbt wirdBaseException
.Nicht spezifisch für Python.
Der springende Punkt bei Ausnahmen ist, das Problem so nahe wie möglich an der Stelle zu behandeln, an der es verursacht wurde.
Sie behalten also den Code, der unter außergewöhnlichen Umständen das Problem und die Auflösung "nebeneinander" auslösen könnte.
Die Sache ist, dass Sie nicht alle Ausnahmen kennen können, die von einem Code ausgelöst werden könnten. Alles, was Sie wissen können, ist, dass Sie, wenn es sich um eine Ausnahme handelt, bei der eine Datei nicht gefunden wurde, diese abfangen und den Benutzer auffordern können, eine Ausnahme zu erhalten, die die Funktion ausführt oder abbricht.
Wenn Sie versuchen, das zu umgehen, wird unabhängig davon, welches Problem in Ihrer Dateiroutine aufgetreten ist (schreibgeschützt, Berechtigungen, Benutzerkontensteuerung, nicht wirklich ein PDF usw.), jeder in Ihre Datei gefunden, der nicht gefunden wurde, und Ihr Benutzer schreit "aber es ist da, dieser Code ist Mist"
Jetzt gibt es einige Situationen, in denen Sie vielleicht alles fangen, aber sie sollten bewusst ausgewählt werden.
Sie werden abgefangen, machen eine lokale Aktion rückgängig (z. B. das Erstellen oder Sperren einer Ressource (Öffnen einer Datei zum Schreiben zum Schreiben), dann lösen Sie die Ausnahme erneut aus, um sie auf einer höheren Ebene zu behandeln).
Dem anderen ist es egal, warum es schief gelaufen ist. Zum Beispiel drucken. Möglicherweise haben Sie einen Haken, der besagt, dass es ein Problem mit Ihrem Drucker gibt. Bitte klären Sie es und beenden Sie die Anwendung nicht deswegen. Ähnlich vergeblich, wenn Ihr Code eine Reihe separater Aufgaben nach einem Zeitplan ausführt, möchten Sie nicht, dass das Ganze stirbt, da eine der Aufgaben fehlgeschlagen ist.
Hinweis Wenn Sie die oben genannten Schritte ausführen, kann ich keine Ausnahmeprotokollierung empfehlen, z. B. versuchen Sie, das Ende des Fangprotokolls hoch genug zu ermitteln.
quelle
Sie werden damit auch zB Control-C fangen, also tun Sie es nicht, es sei denn, Sie "werfen" es erneut. In diesem Fall sollten Sie jedoch lieber "finally" verwenden.
quelle
Immer geben Sie den Ausnahmetyp, gibt es viele Typen , die Sie nicht fangen wollen, wie
SyntaxError
,KeyboardInterrupt
,MemoryError
usw.quelle
except Exception:
die oben genannten Typen vermeiden, die wir nicht fangen wollen?except Exception
ist gut.except Exception
fängtSyntaxError
undMemoryError
weil es ihre Basisklasse ist.KeyboardInterrupt
,SystemExit
(sys.exit()
ausgelöst von ) werden nicht abgefangen (es handelt sich um unmittelbare BaseException-Unterklassen)Hier sind die Orte, an denen ich außer ohne Typ benutze
Das ist die Hauptverwendung in meinem Code für ungeprüfte Ausnahmen
Ich füge dies immer hinzu, damit der Produktionscode keine Stapelspuren verschüttet
Ich habe zwei Möglichkeiten:
Ich bevorzuge es so, ich finde es einfacher zu erkennen, welche Ausnahmen angemessen abgefangen werden sollten: Ich "sehe" das Problem besser, wenn eine Ausnahme niedrigerer Ebene von einer höheren Ebene protokolliert wird
Einige Mitarbeiter bevorzugen diesen Weg, da Ausnahmen auf niedrigerer Ebene in Funktionen auf niedrigerer Ebene beibehalten werden, zu denen sie "gehören".
quelle
Versuche dies:
Ich habe die Antwort von diesem Link erhalten, wenn jemand anderes auf dieses Problem stößt
quelle