"Ich weiß nicht, ob es aus Unwissenheit ist, aber ich mag diese Art der Programmierung nicht, da sie Ausnahmen verwendet, um die Flusskontrolle durchzuführen."
In der Python-Welt ist die Verwendung von Ausnahmen für die Flusskontrolle üblich und normal.
Sogar die Python-Kernentwickler verwenden Ausnahmen für die Flusskontrolle, und dieser Stil ist stark in die Sprache eingebunden (dh das Iteratorprotokoll verwendet StopIteration , um die Beendigung der Schleife zu signalisieren).
Darüber hinaus wird der Try-Except-Style verwendet, um die Rennbedingungen zu verhindern, die einigen der "Look-before-you-leap" -Konstrukte inhärent sind . Wenn Sie beispielsweise os.path.exists testen, erhalten Sie Informationen, die zum Zeitpunkt der Verwendung möglicherweise veraltet sind. Ebenso gibt Queue.full Informationen zurück, die möglicherweise veraltet sind. Der Try-außer-else-Stil erzeugt in diesen Fällen zuverlässigeren Code.
"Nach meinem Verständnis sind Ausnahmen keine Fehler, sie sollten nur für außergewöhnliche Bedingungen verwendet werden."
In einigen anderen Sprachen spiegelt diese Regel ihre kulturellen Normen wider, wie sie sich in ihren Bibliotheken widerspiegeln. Die "Regel" basiert teilweise auch auf Leistungsüberlegungen für diese Sprachen.
Die Python-Kulturnorm ist etwas anders. In vielen Fällen Sie müssen Ausnahmen für Steuerfluss verwenden. Außerdem verlangsamt die Verwendung von Ausnahmen in Python den umgebenden Code und den aufrufenden Code nicht wie in einigen kompilierten Sprachen (dh CPython implementiert bereits Code für die Ausnahmeprüfung bei jedem Schritt, unabhängig davon, ob Sie tatsächlich Ausnahmen verwenden oder nicht).
Mit anderen Worten, Ihr Verständnis, dass "Ausnahmen für das Außergewöhnliche sind", ist eine Regel, die in einigen anderen Sprachen sinnvoll ist, jedoch nicht für Python.
"Wenn es jedoch in der Sprache selbst enthalten ist, muss es einen guten Grund dafür geben, nicht wahr?"
Ausnahmen sind nicht nur hilfreich, um Rennbedingungen zu vermeiden, sondern auch sehr nützlich, um die Fehlerbehandlung außerhalb von Schleifen durchzuführen. Dies ist eine notwendige Optimierung in interpretierten Sprachen, die keine automatische schleifeninvariante Codebewegung aufweisen .
Außerdem können Ausnahmen den Code in häufigen Situationen, in denen die Fähigkeit zur Behandlung eines Problems weit entfernt von dem Ort ist, an dem das Problem aufgetreten ist, erheblich vereinfachen. Beispielsweise ist es üblich, dass Benutzeroberflächencode der obersten Ebene Code für die Geschäftslogik aufruft, der wiederum Routinen auf niedriger Ebene aufruft. Situationen, die in Routinen auf niedriger Ebene auftreten (z. B. doppelte Datensätze für eindeutige Schlüssel bei Datenbankzugriffen), können nur im Code der obersten Ebene behandelt werden (z. B. Aufforderung an den Benutzer nach einem neuen Schlüssel, der nicht mit vorhandenen Schlüsseln in Konflikt steht). Die Verwendung von Ausnahmen für diese Art von Kontrollfluss ermöglicht es den Routinen der mittleren Ebene, das Problem vollständig zu ignorieren und sich von diesem Aspekt der Flusskontrolle gut zu entkoppeln.
Es gibt hier einen schönen Blog-Beitrag über die Unentbehrlichkeit von Ausnahmen .
Siehe auch diese Antwort zum Stapelüberlauf: Sind Ausnahmen wirklich für außergewöhnliche Fehler?
"Was ist der Grund für den Versuch - außer - sonst zu existieren?"
Die else-Klausel selbst ist interessant. Es wird ausgeführt, wenn es keine Ausnahme gibt, jedoch vor der finally-Klausel. Das ist sein Hauptzweck.
Ohne die else-Klausel wäre die einzige Möglichkeit, zusätzlichen Code vor der Finalisierung auszuführen, die ungeschickte Praxis, den Code zur try-Klausel hinzuzufügen. Das ist umständlich, da die Gefahr besteht, dass Ausnahmen in Code ausgelöst werden, die nicht durch den try-Block geschützt werden sollten.
Der Anwendungsfall, vor der Finalisierung zusätzlichen ungeschützten Code auszuführen, tritt nicht sehr häufig auf. Erwarten Sie also nicht viele Beispiele im veröffentlichten Code. Es ist etwas selten.
Ein weiterer Anwendungsfall für die else-Klausel besteht darin, Aktionen auszuführen, die ausgeführt werden müssen, wenn keine Ausnahme auftritt, und die nicht auftreten, wenn Ausnahmen behandelt werden. Zum Beispiel:
recip = float('Inf')
try:
recip = 1 / f(x)
except ZeroDivisionError:
logging.info('Infinite result')
else:
logging.info('Finite result')
Ein anderes Beispiel tritt bei unittest Läufern auf:
try:
tests_run += 1
run_testcase(case)
except Exception:
tests_failed += 1
logging.exception('Failing test case: %r', case)
print('F', end='')
else:
logging.info('Successful test case: %r', case)
print('.', end='')
Schließlich wird eine else-Klausel in einem try-Block am häufigsten für eine gewisse Verschönerung verwendet (Ausrichten der außergewöhnlichen Ergebnisse und nicht außergewöhnlichen Ergebnisse auf derselben Einrückungsstufe). Diese Verwendung ist immer optional und nicht unbedingt erforderlich.
Mit einem
try
Block können Sie einen erwarteten Fehler behandeln. Derexcept
Block sollte nur Ausnahmen abfangen, für die Sie bereit sind. Wenn Sie einen unerwarteten Fehler behandeln, kann Ihr Code das Falsche tun und Fehler verbergen.Eine
else
Klausel wird ausgeführt, wenn keine Fehler aufgetreten sind. Wenn Sie diesen Code nicht imtry
Block ausführen, vermeiden Sie, dass ein unerwarteter Fehler auftritt. Auch hier kann das Abfangen eines unerwarteten Fehlers Fehler verbergen.Beispiel
Zum Beispiel:
Die Suite "try, without" enthält zwei optionale Klauseln
else
undfinally
. So ist es eigentlichtry-except-else-finally
.else
wird nur ausgewertet, wenn es keine Ausnahme vomtry
Block gibt. Dies ermöglicht es uns, den komplizierteren Code unten zu vereinfachen:Wenn wir also eine
else
mit der Alternative vergleichen (was zu Fehlern führen kann), stellen wir fest, dass dadurch die Codezeilen reduziert werden und wir eine besser lesbare, wartbare und weniger fehlerhafte Codebasis haben können.finally
finally
wird ausgeführt, egal was passiert, auch wenn eine andere Zeile mit einer return-Anweisung ausgewertet wird.Mit Pseudocode aufgeschlüsselt
Es kann hilfreich sein, dies in der kleinstmöglichen Form, die alle Funktionen demonstriert, mit Kommentaren aufzuschlüsseln. Angenommen, dieser syntaktisch korrekte (aber nicht ausführbare, wenn die Namen nicht definiert sind) Pseudocode befindet sich in einer Funktion.
Zum Beispiel:
Es ist wahr, dass wir den Code stattdessen in den Block des Blocks aufnehmen könnten , wo er ausgeführt würde, wenn es keine Ausnahmen gäbe, aber was ist, wenn dieser Code selbst eine Ausnahme der Art auslöst, die wir abfangen? Wenn Sie es im Block belassen, wird dieser Fehler ausgeblendet.
else
try
try
Wir möchten die Codezeilen im
try
Block minimieren, um zu vermeiden, dass Ausnahmen abgefangen werden, die wir nicht erwartet haben. Dabei möchten wir, dass unser Code lautstark fehlschlägt, wenn er fehlschlägt. Dies ist eine bewährte Methode .In Python sind die meisten Ausnahmen Fehler.
Wir können die Ausnahmehierarchie mit pydoc anzeigen. Zum Beispiel in Python 2:
oder Python 3:
Wird uns die Hierarchie geben. Wir können sehen, dass die meisten Arten von
Exception
Fehlern sind, obwohl Python einige davon für Dinge wie das Beenden vonfor
Schleifen (StopIteration
) verwendet. Dies ist die Hierarchie von Python 3:Ein Kommentator fragte:
Nein, Sie geben die Ausnahme nicht zurück, sondern erhöhen sie nur mit einem Bare
raise
, um die Stapelverfolgung beizubehalten .In Python 3 können Sie auch eine neue Ausnahme auslösen und die Rückverfolgung mit Ausnahmeverkettung beibehalten:
Ich gehe hier auf meine Antwort ein .
quelle
raise
oder einer Ausnahmeverkettung wiederholen - aber das ist mehr zum Thema und wird hier behandelt: stackoverflow.com/q/2052390/541136 - Ich werde diese Kommentare wahrscheinlich entfernen, nachdem ich gesehen, du hast sie gesehen.Python schließt sich nicht der Idee an, dass Ausnahmen nur in Ausnahmefällen verwendet werden sollten. Tatsächlich lautet die Redewendung "Bitte um Vergebung, nicht um Erlaubnis" . Dies bedeutet, dass die Verwendung von Ausnahmen als Routineteil Ihrer Flusskontrolle durchaus akzeptabel ist und in der Tat empfohlen wird.
Dies ist im Allgemeinen eine gute Sache, da auf diese Weise einige Probleme vermieden werden (als offensichtliches Beispiel werden häufig die Rennbedingungen vermieden) und der Code dadurch tendenziell besser lesbar wird.
Stellen Sie sich vor, Sie haben eine Benutzereingabe, die verarbeitet werden muss, aber eine Standardeinstellung, die bereits verarbeitet wird. Die
try: ... except: ... else: ...
Struktur sorgt für sehr gut lesbaren Code:Vergleichen Sie, wie es in anderen Sprachen funktionieren könnte:
Beachten Sie die Vorteile. Es ist nicht erforderlich, den Wert zu überprüfen und separat zu analysieren. Sie werden einmal ausgeführt. Der Code folgt auch einer logischeren Abfolge. Der Hauptcodepfad ist zuerst, gefolgt von "Wenn dies nicht funktioniert, tun Sie dies".
Das Beispiel ist natürlich ein wenig erfunden, aber es zeigt, dass es Fälle für diese Struktur gibt.
quelle
Die Antwort darauf ist, dass es kontextabhängig ist. Wenn du das tust:
Es zeigt, dass Sie Python nicht sehr gut kennen. Diese Funktionalität ist in der
dict.get
Methode gekapselt :Der
try
/except
-Block ist eine viel visuell überladenere und ausführlichere Art zu schreiben, was mit einer atomaren Methode effizient in einer einzelnen Zeile ausgeführt werden kann. Es gibt andere Fälle, in denen dies zutrifft.Dies bedeutet jedoch nicht, dass wir jede Ausnahmebehandlung vermeiden sollten. In einigen Fällen ist es bevorzugt, Rennbedingungen zu vermeiden. Überprüfen Sie nicht, ob eine Datei vorhanden ist, sondern versuchen Sie einfach, sie zu öffnen und den entsprechenden IOError abzufangen. Versuchen Sie der Einfachheit und Lesbarkeit halber, dies zu kapseln oder als apropos herauszufiltern.
Lesen Sie das Zen von Python und verstehen Sie, dass es Prinzipien gibt, die unter Spannung stehen, und hüten Sie sich vor Dogmen, die sich zu stark auf eine der darin enthaltenen Aussagen stützen.
quelle
Sehen Sie sich das folgende Beispiel an, das alles über try-außer-else-finally veranschaulicht:
Implementieren Sie es und kommen Sie vorbei:
quelle
Sie sollten vorsichtig sein, wenn Sie den finally-Block verwenden, da dies nicht dasselbe ist wie die Verwendung eines else-Blocks beim Versuch, außer. Der finally-Block wird unabhängig vom Ergebnis des Versuchs ausgeführt, außer.
Wie jeder bemerkt hat, führt die Verwendung des else-Blocks dazu, dass Ihr Code besser lesbar ist und nur ausgeführt wird, wenn keine Ausnahme ausgelöst wird
quelle
Wann immer Sie dies sehen:
Oder sogar das:
Betrachten Sie stattdessen Folgendes:
quelle
Nur weil sonst niemand diese Meinung gepostet hat, würde ich sagen
Im Gegensatz zu den Schlüsselwörtern
try
,except
undfinally
die Bedeutung derelse
ist Klausel nicht selbstverständlich; es ist weniger lesbar. Da es nicht sehr oft verwendet wird, möchten Benutzer, die Ihren Code lesen, die Dokumente überprüfen, um sicherzustellen, dass sie verstehen, was los ist.(Ich schreibe diese Antwort genau, weil ich eine
try/except/else
in meiner Codebasis gefunden habe und sie einen WTF-Moment verursachte und mich zwang, etwas zu googeln).Also, wo immer ich Code wie das OP-Beispiel sehe:
Ich würde es vorziehen, umzugestalten
Die explizite Rückgabe <1> zeigt deutlich, dass wir im Ausnahmefall mit der Arbeit fertig sind
<2> Als netter kleiner Nebeneffekt wird der Code, der sich früher im
else
Block befand, um eine Ebene reserviert.quelle
Dies ist mein einfacher Ausschnitt darüber, wie man den Try-außer-else-finally-Block in Python versteht:
Versuchen wir es mit div 1/1:
Versuchen wir es mit div 1/0
quelle
OP, SIE SIND RICHTIG. Das andere nach try / außer in Python ist hässlich . es führt zu einem anderen Flusssteuerungsobjekt, für das keines benötigt wird:
Ein völlig klares Äquivalent ist:
Dies ist weitaus klarer als eine else-Klausel. Das else nach try / exception wird nicht häufig geschrieben, daher dauert es einen Moment, um die Auswirkungen herauszufinden.
Nur weil Sie etwas tun können, heißt das nicht, dass Sie etwas tun sollten.
Sprachen wurden viele Funktionen hinzugefügt, weil jemand dachte, dass dies nützlich sein könnte. Das Problem ist, je mehr Funktionen, desto weniger klar und offensichtlich sind die Dinge, weil die Leute diese Schnickschnack normalerweise nicht benutzen.
Nur meine 5 Cent hier. Ich muss mitkommen und eine Menge Code bereinigen, der im ersten Jahr von College-Entwicklern geschrieben wurde, die denken, dass sie schlau sind und Code auf eine überaus enge, übereffiziente Art und Weise schreiben wollen, wenn dies nur zu einem Chaos führt um später zu versuchen, zu lesen / zu ändern. Ich stimme jeden Tag und sonntags zweimal für Lesbarkeit.
quelle
print
Aussage schlägt fehl. Was passiert, wennx = blah()
a zurückgegebenstr
wird, Ihre Druckanweisung jedoch lautetprint 'just succeeded with blah. x == %d' % x
? Jetzt haben Sie einTypeError
Wesen erzeugt, in dem Sie nicht bereit sind, mit einem umzugehen. Siex = blah()
suchen nach der Quelle der Ausnahme, und sie ist noch nicht einmal vorhanden. Ich habe dies (oder das Äquivalent) mehr als einmal getan, woelse
mich das davon abgehalten hätte, diesen Fehler zu machen. Jetzt weiß ich es besser. :-Delse
Klausel ist keine schöne Aussage, und bis Sie daran gewöhnt sind, ist sie nicht intuitiv. Aber dann war es auch nicht,finally
als ich anfing, es zu benutzen ...else
Klausel nicht von der erfasst werdenexcept
.x = blah()
, um die Quelle der Ausnahme zu finden".traceback
Warum sollten Sie die Ausnahmequelle von einem falschen Ort aus untersuchen?