Ich arbeite an dem Buch "Head First Python" (es ist meine Sprache, die ich in diesem Jahr lernen muss) und ich komme zu einem Abschnitt, in dem über zwei Codetechniken gestritten wird:
Die Behandlung von "Checking First vs Exception".
Hier ist ein Beispiel des Python-Codes:
# Checking First
for eachLine in open("../../data/sketch.txt"):
if eachLine.find(":") != -1:
(role, lineSpoken) = eachLine.split(":",1)
print("role=%(role)s lineSpoken=%(lineSpoken)s" % locals())
# Exception handling
for eachLine in open("../../data/sketch.txt"):
try:
(role, lineSpoken) = eachLine.split(":",1)
print("role=%(role)s lineSpoken=%(lineSpoken)s" % locals())
except:
pass
Das erste Beispiel befasst sich direkt mit einem Problem in der .split
Funktion. Der zweite lässt nur den Ausnahmehandler damit umgehen (und ignoriert das Problem).
Sie argumentieren im Buch, die Ausnahmebehandlung zu verwenden, anstatt zuerst zu prüfen. Das Argument ist, dass der Ausnahmecode alle Fehler auffängt, wobei das Überprüfen zuerst nur die Dinge auffängt, über die Sie nachdenken (und Sie die Eckfälle verpassen). Mir wurde beigebracht, zuerst nachzuschauen, also war es mein erster Instinkt, das zu tun, aber ihre Idee ist interessant. Ich hatte nie daran gedacht, die Ausnahmebehandlung für die Bearbeitung von Fällen zu verwenden.
Welches der beiden Verfahren wird allgemein als die bessere Praxis angesehen?
quelle
if -1 == eachLine.find(":"): continue
dann würde der Rest der Schleife auch nicht eingerückt werden.Antworten:
In .NET ist es gängige Praxis, die übermäßige Verwendung von Ausnahmen zu vermeiden. Ein Argument ist die Leistung: In .NET ist das Auslösen einer Ausnahme rechenintensiv.
Ein weiterer Grund, um eine Überbeanspruchung zu vermeiden, besteht darin, dass es sehr schwierig sein kann, Code zu lesen, der zu stark von ihnen abhängt. Joel Spolskys Blog-Eintrag liefert eine gute Beschreibung des Problems.
Im Zentrum des Arguments steht das folgende Zitat:
Persönlich mache ich Ausnahmen, wenn mein Code nicht das kann, wozu er vertraglich verpflichtet ist. Ich benutze normalerweise try / catch, wenn ich mit etwas außerhalb meiner Prozessgrenze zu tun habe, zum Beispiel mit einem SOAP-Aufruf, einem Datenbankaufruf, einer Datei-E / A oder einem Systemaufruf. Ansonsten versuche ich defensiv zu codieren. Es ist keine feste Regel, aber es ist eine allgemeine Praxis.
Scott Hanselman schreibt hier auch über Ausnahmen in .NET . In diesem Artikel beschreibt er einige Faustregeln in Bezug auf Ausnahmen. Mein Lieblings?
quelle
Insbesondere in Python wird es normalerweise als besser angesehen, die Ausnahme abzufangen. Im Vergleich zu Look Before You Leap (LBYL) wird es als einfacher empfunden, um Vergebung zu bitten als um Erlaubnis (EAFP). Es gibt Fälle, in denen LBYL in einigen Fällen subtile Fehler verursacht .
Seien Sie jedoch vorsichtig mit nackten
except:
Anweisungen sowie mit Overbroad-Ausnahmen, da sie beide auch Fehler maskieren können - so etwas wäre besser:quelle
try
/ zu verwenden,except
anstatt eine Bedingung einzurichten, für die "Folgendes wahrscheinlich eine Ausnahme auslöst ", und die Python-Praxis ist definitiv die erstere zu bevorzugen. Es tut nicht weh, dass dieser Ansatz hier (wahrscheinlich) effizienter ist, da Sie in dem Fall, in dem die Aufteilung erfolgreich ist, die Saite nur einmal laufen. Was die Fragesplit
betrifft , ob hier eine Ausnahme ausgelöst werden soll, würde ich sagen, dass dies definitiv der Fall sein sollte. Eine gängige Regel ist, dass Sie eine Ausnahme auslösen sollten, wenn Sie nicht das tun können, was Ihr Name sagt, und Sie nicht an einem fehlenden Trennzeichen trennen können.Ein pragmatischer Ansatz
Sie sollten aber bis zu einem gewissen Punkt defensiv sein. Sie sollten die Ausnahmebehandlung aber auf einen Punkt schreiben. Ich werde das Webprogrammieren als Beispiel verwenden, weil ich hier wohne.
Dies ist aus Erfahrung in großen Teamszenarien.
Eine Analogie
Stellen Sie sich vor, Sie hätten die ganze Zeit einen Raumanzug in der ISS getragen. Es würde schwer sein, auf die Toilette zu gehen oder überhaupt zu essen. Es wäre super sperrig, sich innerhalb des Weltraummoduls zu bewegen. Es wäre scheiße. So ähnlich ist es, eine Reihe von Versuchsfängen in Ihren Code zu schreiben. Sie müssen irgendwann wissen, wo Sie sagen: Hey, ich habe die ISS gesichert, und meine Astronauten sind in Ordnung. Es ist also einfach nicht praktisch, für jedes Szenario, das möglicherweise passieren könnte, einen Raumanzug zu tragen.
quelle
Das Hauptargument des Buches ist, dass die Ausnahmeversion des Codes besser ist, da sie alles auffängt, was Sie möglicherweise übersehen hätten, wenn Sie versucht hätten, Ihre eigene Fehlerprüfung zu schreiben.
Ich denke, diese Aussage trifft nur unter ganz bestimmten Umständen zu - wobei es Ihnen egal ist, ob die Ausgabe korrekt ist.
Es besteht kein Zweifel, dass das Auslösen von Ausnahmen eine solide und sichere Praxis ist. Sie sollten dies tun, wenn Sie das Gefühl haben, dass sich im aktuellen Status des Programms etwas befindet, mit dem Sie (als Entwickler) nicht umgehen können oder wollen.
In Ihrem Beispiel geht es jedoch darum , Ausnahmen einzufangen . Wenn Sie eine Ausnahme feststellen , schützen Sie sich nicht vor Szenarien, die Sie möglicherweise übersehen haben. Sie tun genau das Gegenteil: Sie gehen davon aus, dass Sie kein Szenario übersehen haben, das diese Art von Ausnahme verursacht haben könnte, und sind daher zuversichtlich, dass es in Ordnung ist, sie abzufangen (und somit zu verhindern, dass das Programm beendet wird, wie es jede unaufgefangene Ausnahme tun würde).
Wenn Sie den
ValueError
Ausnahmeansatz verwenden und eine Ausnahme sehen, überspringen Sie eine Zeile. Bei Verwendung des herkömmlichen Ansatzes ohne Ausnahmen wird die Anzahl der zurückgegebenen Werte von gezähltsplit
, und bei weniger als 2 wird eine Zeile übersprungen. Sollten Sie sich mit dem Ausnahmeansatz sicherer fühlen, da Sie möglicherweise einige andere "Fehler" -Situationen bei Ihrer herkömmlichen Fehlerprüfung vergessen haben undexcept ValueError
diese für Sie abfangen würden?Dies hängt von der Art Ihres Programms ab.
Wenn Sie beispielsweise einen Webbrowser oder einen Videoplayer schreiben, sollte ein Problem mit Eingaben nicht dazu führen, dass er mit einer nicht erfassten Ausnahme abstürzt. Es ist weitaus besser, etwas aus der Ferne Vernünftiges (auch wenn es streng genommen falsch ist) auszugeben, als zu beenden.
Wenn Sie eine Anwendung schreiben, bei der es auf Korrektheit ankommt (z. B. Geschäfts- oder Entwicklungssoftware), ist dies ein schrecklicher Ansatz. Wenn Sie ein ausgelöstes Szenario vergessen
ValueError
haben, können Sie dieses unbekannte Szenario im Stillen ignorieren und einfach die Zeile überspringen. So entstehen sehr subtile und kostspielige Fehler in der Software.Sie könnten denken, dass die einzige Möglichkeit, die Sie
ValueError
in diesem Code sehen können, darin besteht,split
nur einen Wert (anstelle von zwei) zurückzugeben. Aber was ist, wenn Ihreprint
Anweisung später einen Ausdruck verwendet, derValueError
unter bestimmten Bedingungen ausgelöst wird? Dies führt dazu, dass Sie einige Zeilen überspringen, nicht weil sie fehlen:
, sondern weil sieprint
nicht funktionieren. Dies ist ein Beispiel für einen subtilen Fehler, auf den ich mich früher bezogen habe - Sie würden nichts bemerken, sondern nur einige Zeilen verlieren.Ich empfehle, keine Ausnahmen im Code abzufangen (aber nicht zu erhöhen!), Bei denen das Erzeugen einer falschen Ausgabe schlechter ist als das Beenden. Das einzige Mal, dass ich eine Ausnahme in einem solchen Code abfange, ist, wenn ich einen wirklich trivialen Ausdruck habe, sodass ich leicht überlegen kann, was die einzelnen möglichen Ausnahmetypen verursachen kann.
Die Auswirkungen der Verwendung von Ausnahmen auf die Leistung sind (in Python) trivial, es sei denn, Ausnahmen treten häufig auf.
Wenn Sie Ausnahmen verwenden, um routinemäßig auftretende Bedingungen zu behandeln, können Sie in einigen Fällen enorme Leistungskosten zahlen. Angenommen, Sie führen einen Befehl aus der Ferne aus. Sie können überprüfen, ob Ihr Befehlstext mindestens die Mindestvalidierung (z. B. Syntax) besteht. Oder Sie können warten, bis eine Ausnahme ausgelöst wird (was erst geschieht, nachdem der Remoteserver Ihren Befehl analysiert und ein Problem damit gefunden hat). Ersteres ist offensichtlich um Größenordnungen schneller. Ein weiteres einfaches Beispiel: Sie können überprüfen, ob eine Zahl null bis zehn Mal schneller ist als der Versuch, die Division auszuführen, und dann die ZeroDivisionError-Ausnahme abfangen.
Diese Überlegungen spielen nur eine Rolle, wenn Sie häufig fehlerhafte Befehlszeichenfolgen an Remoteserver senden oder Argumente mit dem Wert Null empfangen, die Sie für die Aufteilung verwenden.
Hinweis: Ich gehe davon aus, dass Sie
except ValueError
anstelle des Gerechten verwenden würdenexcept
. Wie andere betonten, und wie das Buch selbst auf einigen Seiten sagt, sollten Sie niemals nackte verwendenexcept
.Ein weiterer Hinweis: Der richtige Ansatz ohne Ausnahmen besteht darin, die Anzahl der von zurückgegebenen Werte zu zählen
split
, anstatt zu suchen:
. Letzteres ist viel zu langsam, da es die von Ihnen geleistete Arbeit wiederholtsplit
und die Ausführungszeit möglicherweise fast verdoppelt.quelle
Wenn Sie wissen, dass eine Anweisung zu einem ungültigen Ergebnis führen kann, testen Sie dies in der Regel und gehen Sie damit um. Verwenden Sie Ausnahmen für Dinge, die Sie nicht erwarten. Zeug, das "außergewöhnlich" ist. Es macht den Code im vertraglichen Sinne klarer ("sollte nicht null sein" als Beispiel).
quelle
Verwenden Sie, was immer gut funktioniert ..
Sowohl die Ausnahmebehandlung als auch die defensive Programmierung sind verschiedene Arten, die gleiche Absicht auszudrücken.
quelle
TBH, es ist egal, ob Sie den
try/except
Mechaniker oder eineif
Anweisungsprüfung verwenden. Sie sehen sowohl EAFP als auch LBYL in den meisten Python-Baselines, wobei EAFP etwas häufiger vorkommt. Manchmal EAFP ist viel besser lesbar / idiomatische, aber in diesem speziellen Fall denke ich , dass es so oder so in Ordnung.Jedoch...
Ich würde mit Ihrer aktuellen Referenz vorsichtig sein. Ein paar krasse Probleme mit ihrem Code:
with
Idiom verwenden, wenn Sie Dateien in Python lesen: Es gibt nur sehr wenige Ausnahmen. Das ist keiner von ihnen.except
Anweisung, die keine bestimmte Ausnahme abfängt).role, lineSpoken = eachLine.split(":",1)
Ivc hat eine gute Antwort zu diesem und dem EAFP, gibt aber auch den Deskriptor preis.
Die LBYL-Version ist nicht unbedingt so performant wie die EAFP-Version, weshalb das Auslösen von Ausnahmen "in Bezug auf die Leistung teuer" grundsätzlich falsch ist. Es hängt wirklich von der Art der Zeichenfolgen ab, die Sie verarbeiten:
quelle
Grundsätzlich sollte die Ausnahmebehandlung für OOP-Sprachen besser geeignet sein.
Der zweite Punkt ist die Leistung, da Sie nicht
eachLine.find
für jede Zeile ausführen müssen .quelle
Ich denke, defensive Programmierung schadet der Leistung. Sie sollten auch nur die Ausnahmen abfangen, die Sie behandeln möchten, und die Laufzeitumgebung mit der Ausnahme befassen, die Sie nicht behandeln können.
quelle