Warum ist "außer: bestanden" eine schlechte Programmierpraxis?


324

Ich sehe oft Kommentare zu anderen Fragen zum Stapelüberlauf darüber, wie von der Verwendung except: passabgeraten wird. Warum ist das so schlimm? Manchmal ist es mir einfach egal, was die Fehler sind, und ich möchte einfach mit dem Code fortfahren.

try:
    something
except:
    pass

Warum ist die Verwendung eines except: passBlocks schlecht? Was macht es schlecht? Ist es die Tatsache, dass ich passeinen Fehler habe oder dass ich excepteinen Fehler habe?


1
Zumindest würde ich vorschlagen, dass Sie es protokollieren, damit Sie wissen, welche Probleme Sie ignorieren. Verwenden Sie das loggingModul auf DEBUG-Ebene, um zu vermeiden, dass sie in der Produktion ausströmen, aber halten Sie sie in der Entwicklung verfügbar.
pcurry

Antworten:


345

Wie Sie richtig erraten haben, gibt es zwei Seiten: Abfangen eines Fehlers durch Angabe des Ausnahmetyps nach dem Angeben exceptund einfaches Übergeben ohne Fehler .

Meine Erklärung ist "ein bisschen" länger - also zerfällt es wie folgt:

  1. Fange keinen Fehler . Geben Sie immer an, von welchen Ausnahmen Sie sich erholen möchten, und fangen Sie nur diese ab.
  2. Vermeiden Sie es, außer Blöcken vorbeizukommen . Sofern nicht ausdrücklich gewünscht, ist dies normalerweise kein gutes Zeichen.

Aber gehen wir ins Detail:

Fange keinen Fehler

Wenn Sie einen tryBlock verwenden, tun Sie dies normalerweise, weil Sie wissen, dass die Möglichkeit besteht, dass eine Ausnahme ausgelöst wird. Als solches haben Sie auch bereits eine ungefähre Vorstellung davon, was brechen und welche Ausnahme ausgelöst werden kann. In solchen Fällen fangen Sie eine Ausnahme ab, weil Sie sich positiv davon erholen können. Das bedeutet, dass Sie auf die Ausnahme vorbereitet sind und einen alternativen Plan haben, dem Sie im Falle dieser Ausnahme folgen werden.

Wenn Sie beispielsweise den Benutzer auffordern, eine Zahl einzugeben, können Sie die Eingabe konvertieren, mit int()der möglicherweise eine a ausgelöst wird ValueError. Sie können dies leicht wiederherstellen, indem Sie den Benutzer einfach auffordern, es erneut zu versuchen. ValueErrorDaher wäre es ein angemessener Plan , den Benutzer zu fangen und ihn erneut aufzufordern. Ein anderes Beispiel wäre, wenn Sie eine Konfiguration aus einer Datei lesen möchten und diese Datei zufällig nicht vorhanden ist. Da es sich um eine Konfigurationsdatei handelt, haben Sie möglicherweise eine Standardkonfiguration als Fallback, sodass die Datei nicht unbedingt erforderlich ist. Daher FileNotFoundErrorwäre es hier ein guter Plan, eine zu erfassen und einfach die Standardkonfiguration anzuwenden. In beiden Fällen haben wir eine sehr spezifische Ausnahme, die wir erwarten, und wir haben einen ebenso spezifischen Plan, um uns davon zu erholen. Als solche haben wir in jedem Fall ausdrücklich nur except das Bestimmte Ausnahme.

Wenn wir jedoch alles fangen , besteht - zusätzlich zu den Ausnahmen, von denen wir bereit sind, uns zu erholen - auch die Möglichkeit, dass wir Ausnahmen erhalten, die wir nicht erwartet haben und von denen wir uns tatsächlich nicht erholen können. oder sollte sich nicht erholen.

Nehmen wir das Beispiel der Konfigurationsdatei von oben. Im Falle einer fehlenden Datei haben wir gerade unsere Standardkonfiguration angewendet und möglicherweise zu einem späteren Zeitpunkt beschlossen, die Konfiguration automatisch zu speichern (damit die Datei beim nächsten Mal vorhanden ist). Stellen Sie sich jetzt vor, wir bekommen ein IsADirectoryErroroder einPermissionErrorstattdessen. In solchen Fällen wollen wir wahrscheinlich nicht weitermachen; Wir könnten weiterhin unsere Standardkonfiguration anwenden, aber später können wir die Datei nicht speichern. Und es ist wahrscheinlich, dass der Benutzer auch eine benutzerdefinierte Konfiguration haben wollte, sodass die Verwendung der Standardwerte wahrscheinlich nicht erwünscht ist. Daher möchten wir den Benutzer sofort darüber informieren und wahrscheinlich auch die Programmausführung abbrechen. Aber das wollen wir nicht irgendwo tief in einem kleinen Codeteil tun. Dies ist von Bedeutung auf Anwendungsebene, daher sollte es oben behandelt werden. Lassen Sie also die Ausnahme in die Luft sprudeln.

Ein weiteres einfaches Beispiel wird auch im Python 2-Redewendungsdokument erwähnt. Hier existiert ein einfacher Tippfehler im Code, der dazu führt, dass er kaputt geht. Weil wir jede Ausnahme abfangen, fangen wir auch NameErrors und SyntaxErrors . Beides sind Fehler, die uns allen beim Programmieren passieren. und beides sind Fehler, die wir beim Versand des Codes unbedingt nicht berücksichtigen möchten. Aber weil wir diese auch gefangen haben, werden wir nicht einmal wissen, dass sie dort aufgetreten sind, und jede Hilfe verlieren, um sie korrekt zu debuggen.

Es gibt aber auch gefährlichere Ausnahmen, auf die wir wahrscheinlich nicht vorbereitet sind. Zum Beispiel ist SystemError normalerweise etwas, das selten vorkommt und das wir nicht wirklich planen können. es bedeutet, dass etwas Komplizierteres vor sich geht, was uns wahrscheinlich daran hindert, die aktuelle Aufgabe fortzusetzen.

In jedem Fall ist es sehr unwahrscheinlich, dass Sie in einem kleinen Teil des Codes auf alles vorbereitet sind. Hier sollten Sie also nur die Ausnahmen abfangen, auf die Sie vorbereitet sind. Einige Leute schlagen vor, zumindest zu fangen, Exceptionda es keine Dinge wie SystemExitund KeyboardInterruptdie beabsichtigt sind, Ihre Anwendung zu beenden, aber ich würde argumentieren, dass dies immer noch viel zu unspezifisch ist. Es gibt nur einen Ort, an dem ich persönlich das Fangen akzeptiere Exceptionoder nur irgendeinenAusnahme, und das ist in einem einzelnen globalen Ausnahmebehandler auf Anwendungsebene, der den einzigen Zweck hat, jede Ausnahme zu protokollieren, auf die wir nicht vorbereitet waren. Auf diese Weise können wir immer noch so viele Informationen über unerwartete Ausnahmen behalten, die wir dann verwenden können, um unseren Code zu erweitern, um diese explizit zu behandeln (wenn wir sie wiederherstellen können) oder - im Fehlerfall - Testfälle zu erstellen, um dies sicherzustellen es wird nicht wieder vorkommen. Aber das funktioniert natürlich nur, wenn wir nur die Ausnahmen erwischt haben, die wir bereits erwartet hatten. Diejenigen, die wir nicht erwartet haben, werden natürlich in die Luft sprudeln.

Vermeiden Sie es, außer Blöcken vorbeizukommen

Wenn wir explizit eine kleine Auswahl spezifischer Ausnahmen abfangen, gibt es viele Situationen, in denen es uns gut geht, wenn wir einfach nichts tun. In solchen Fällen except SomeSpecificException: passist es in Ordnung , nur zu haben . Meistens ist dies jedoch nicht der Fall, da wir wahrscheinlich Code für den Wiederherstellungsprozess benötigen (wie oben erwähnt). Dies kann beispielsweise etwas sein, das die Aktion erneut versucht, oder stattdessen einen Standardwert einrichten.

Wenn dies jedoch nicht der Fall ist, zum Beispiel weil unser Code bereits so strukturiert ist, dass er wiederholt wird, bis er erfolgreich ist, ist es gut genug, nur zu übergeben. In unserem Beispiel von oben möchten wir den Benutzer möglicherweise bitten, eine Nummer einzugeben. Da wir wissen, dass Benutzer nicht gerne das tun, wonach wir sie fragen, setzen wir es möglicherweise zunächst in eine Schleife, sodass es folgendermaßen aussehen kann:

def askForNumber ():
    while True:
        try:
            return int(input('Please enter a number: '))
        except ValueError:
            pass

Da wir es so lange versuchen, bis keine Ausnahme mehr ausgelöst wird, müssen wir im Ausnahmeblock nichts Besonderes tun. Das ist also in Ordnung. Aber natürlich könnte man argumentieren, dass wir dem Benutzer zumindest eine Fehlermeldung anzeigen möchten, um ihm zu sagen, warum er die Eingabe wiederholen muss.

In vielen anderen Fällen exceptist es jedoch ein Zeichen dafür, dass wir nicht wirklich auf die Ausnahme vorbereitet waren, die wir fangen. Versuchen Sie zu vermeiden, nur zu bestehen, es sei denn, diese Ausnahmen sind einfach (wie ValueErroroder TypeError) und der Grund, warum wir bestehen können, liegt auf der Hand. Wenn es wirklich nichts zu tun gibt (und Sie sind sich absolut sicher), sollten Sie einen Kommentar hinzufügen, warum dies der Fall ist. Andernfalls erweitern Sie den Ausnahmeblock, um tatsächlich einen Wiederherstellungscode einzuschließen.

except: pass

Der schlimmste Täter ist jedoch die Kombination von beiden. Dies bedeutet , dass wir fangen bereitwillig jeden Fehler , obwohl wir absolut nicht darauf vorbereitet und wir tun auch nichts darüber. Sie möchten den Fehler zumindest protokollieren und ihn wahrscheinlich erneut erhöhen, um die Anwendung trotzdem zu beenden (es ist unwahrscheinlich, dass Sie nach einem MemoryError wie gewohnt fortfahren können). Wenn Sie jedoch nur weitergeben, bleibt die Anwendung nicht nur am Leben (je nachdem, wo Sie sie abfangen), sondern Sie werfen auch alle Informationen weg, sodass der Fehler nicht erkannt werden kann. Dies gilt insbesondere dann, wenn Sie ihn nicht entdecken.


Das Fazit lautet also: Fangen Sie nur Ausnahmen ab, die Sie wirklich erwarten und von denen Sie sich erholen möchten. Bei allen anderen handelt es sich wahrscheinlich entweder um Fehler, die Sie beheben sollten, oder um etwas, auf das Sie ohnehin nicht vorbereitet sind. Das Übergeben bestimmter Ausnahmen ist in Ordnung, wenn Sie wirklich nichts dagegen tun müssen. In allen anderen Fällen ist es nur ein Zeichen von Vermutung und Faulheit. Und das wollen Sie auf jeden Fall beheben.


1
"Sie möchten den Fehler zumindest protokollieren und ihn wahrscheinlich erneut erhöhen, um die Anwendung trotzdem zu beenden." Können Sie zeigen, wie Sie eine Ausnahme "erneuern" können, damit sie auch nach dem Auffangen weiter sprudelt? Dies scheint mir nützlich zu sein, um einige benutzerdefinierte Fehlermeldungen hinzuzufügen, während die Ausnahme die Anwendung zum Beenden zwingt.
Gabriel Staples

1
Dies hilft zu verdeutlichen: Sie verwenden die Decke except, rufen dann aber raiseohne Argumente auf, um die Ausnahme weiterhin in die Luft sprudeln zu lassen und die Anwendung zu beenden. Ich mag es: ianbicking.org/blog/2007/09/re-raising-exceptions.html . Sieht nach einer soliden Ausnahme von der Regel aus, dass die Decke nicht verwendet wird except.
Gabriel Staples

1
@GabrielStaples Ja, eine abgefangene Ausnahme kann mit erneut ausgelöst werden raise. Normalerweise tun Sie dies jedoch nur an wenigen Stellen in Ihrer Anwendung, um die Ausnahme zu protokollieren.
stupsen

Das ist großartig, vermeiden Sie es, außer Blöcken vorbeizukommen. Ich würde sagen, dass Sie alles tun, was verständlicher erscheint, insbesondere für andere. Holen Sie sich einen zweiten Satz Python-Augen, um Ihren Code zu überprüfen und festzustellen, ob sie den Block in Frage stellen. Lesbarkeit ist der Schlüssel.
Radtek

262

Das Hauptproblem hierbei ist, dass alle und alle Fehler ignoriert werden: Nicht genügend Speicher, CPU brennt, Benutzer möchte anhalten, Programm möchte beendet werden, Jabberwocky tötet Benutzer.

Das ist viel zu viel. In deinem Kopf denkst du "Ich möchte diesen Netzwerkfehler ignorieren". Wenn etwas Unerwartetes schief geht, wird Ihr Code stillschweigend fortgesetzt und auf völlig unvorhersehbare Weise unterbrochen, die niemand debuggen kann.

Deshalb sollten Sie sich darauf beschränken, nur einige Fehler zu ignorieren und den Rest passieren zu lassen.


75

Das wörtliche Ausführen Ihres Pseudocodes gibt nicht einmal einen Fehler:

try:
    something
except:
    pass

als ob es ein vollkommen gültiger Code wäre, anstatt a zu werfen NameError. Ich hoffe das ist nicht was du willst.


51

Warum ist "außer: bestanden" eine schlechte Programmierpraxis?

Warum ist das so schlimm?

try:
    something
except:
    pass

Dadurch werden alle möglichen Ausnahmen erfasst, einschließlich GeneratorExit, KeyboardInterruptund SystemExit-. Dies sind Ausnahmen, die Sie wahrscheinlich nicht abfangen möchten. Es ist das gleiche wie fangen BaseException.

try:
    something
except BaseException:
    pass

Ältere Versionen der Dokumentation sagen :

Da jeder Fehler in Python eine Ausnahme auslöst, except:können bei der Verwendung viele Programmierfehler wie Laufzeitprobleme aussehen, was den Debugging-Prozess behindert.

Python-Ausnahmehierarchie

Wenn Sie eine übergeordnete Ausnahmeklasse abfangen, erfassen Sie auch alle untergeordneten Klassen. Es ist viel eleganter, nur die Ausnahmen zu erfassen, auf die Sie vorbereitet sind.

Hier ist die Python 3- Ausnahmehierarchie - möchten Sie sie wirklich alle fangen?:

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
           +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning

Tu das nicht

Wenn Sie diese Form der Ausnahmebehandlung verwenden:

try:
    something
except: # don't just do a bare except!
    pass

Dann können Sie Ihren somethingBlock nicht mit Strg-C unterbrechen . Ihr Programm übersieht jede mögliche Ausnahme innerhalb des tryCodeblocks.

Hier ist ein weiteres Beispiel, das das gleiche unerwünschte Verhalten aufweist:

except BaseException as e: # don't do this either - same as bare!
    logging.info(e)

Versuchen Sie stattdessen, nur die spezifische Ausnahme abzufangen, von der Sie wissen, dass Sie sie suchen. Wenn Sie beispielsweise wissen, dass bei einer Konvertierung möglicherweise ein Wertfehler auftritt:

try:
    foo = operation_that_includes_int(foo)
except ValueError as e:
    if fatal_condition(): # You can raise the exception if it's bad,
        logging.info(e)   # but if it's fatal every time,
        raise             # you probably should just not catch it.
    else:                 # Only catch exceptions you are prepared to handle.
        foo = 0           # Here we simply assign foo to 0 and continue. 

Weitere Erläuterung mit einem anderen Beispiel

Möglicherweise tun Sie dies, weil Sie Web-Scraping durchgeführt haben und sagen, a UnicodeError, aber weil Sie den breitesten Ausnahmefang verwendet haben, wird Ihr Code, der möglicherweise andere grundlegende Fehler aufweist, versuchen, vollständig ausgeführt zu werden, wodurch Bandbreite verschwendet wird , Verarbeitungszeit, Verschleiß Ihrer Geräte, Speichermangel, Sammeln von Mülldaten usw.

Wenn andere Leute Sie zum Ausfüllen auffordern, damit sie sich auf Ihren Code verlassen können, fühle ich mich gezwungen, einfach alles zu erledigen. Wenn Sie jedoch bereit sind, bei der Entwicklung geräuschvoll zu scheitern, haben Sie die Möglichkeit, Probleme zu beheben, die möglicherweise nur zeitweise auftreten, aber langfristig kostspielige Fehler darstellen.

Mit einer genaueren Fehlerbehandlung kann Ihr Code robuster sein.


31
>>> import this

Das Zen von Python von Tim Peters

Schön ist besser als hässlich.
Explizit ist besser als implizit.
Einfach ist besser als komplex.
Komplex ist besser als kompliziert.
Wohnung ist besser als verschachtelt.
Spärlich ist besser als dicht.
Lesbarkeit zählt.
Sonderfälle sind nicht speziell genug, um gegen die Regeln zu verstoßen.
Obwohl Praktikabilität die Reinheit übertrifft.
Fehler sollten niemals stillschweigend vergehen.
Sofern nicht ausdrücklich zum Schweigen gebracht.
Verweigern Sie angesichts von Zweideutigkeiten die Versuchung zu raten.
Es sollte einen - und vorzugsweise nur einen - offensichtlichen Weg geben, dies zu tun.
Obwohl dieser Weg zunächst vielleicht nicht offensichtlich ist, es sei denn, Sie sind Niederländer.
Jetzt ist besser als nie.
Obwohl nie oft besser ist alsgerade jetzt.
Wenn die Implementierung schwer zu erklären ist, ist es eine schlechte Idee.
Wenn die Implementierung leicht zu erklären ist, kann dies eine gute Idee sein.
Namespaces sind eine großartige Idee - lasst uns mehr davon machen!

Also, hier ist meine Meinung. Wenn Sie einen Fehler finden, sollten Sie etwas tun, um ihn zu beheben, z. B. in eine Protokolldatei oder etwas anderes schreiben. Zumindest informiert es Sie, dass früher ein Fehler aufgetreten ist.


64
Hat -1 Argument aus Autorität nicht wirklich erklärt nichts. Autorität kann falsch sein.
Izkata

23
Was @Izkata geschrieben hat, UND eine Zeile darunter schreibt dieselbe Behörde: "Sofern nicht ausdrücklich zum Schweigen gebracht", was genau das ist, außer: pass tut es.
Ofri Raviv

13
@OfriRaviv Nein, geht der Fehler nicht implizit über ? Explizit würde es erforderlich sein, den Fehler zu benennen, der stillschweigend vergehen sollte, dh explizit darüber zu sein . Dies ist nicht was außer: Pass tut.
Chelonian

24

Sie sollten mindestens verwenden except Exception:, um Systemausnahmen wie SystemExitoder zu vermeiden KeyboardInterrupt. Hier ist ein Link zu Dokumenten.

Im Allgemeinen sollten Sie explizit Ausnahmen definieren, die Sie abfangen möchten, um unerwünschte Ausnahmen zu vermeiden. Sie sollten wissen, welche Ausnahmen Sie ignorieren .


13

Erstens verstößt es gegen zwei Prinzipien des Zen of Python :

  • Explizit ist besser als implizit
  • Fehler sollten niemals stillschweigend vergehen

Was es bedeutet, ist, dass Sie Ihren Fehler absichtlich stillschweigend weitergeben. Darüber hinaus wissen Sie nicht, welcher Fehler genau aufgetreten ist, da except: passeine Ausnahme abgefangen wird.

Zweitens, wenn wir versuchen, vom Zen von Python abzuweichen und nur von geistiger Gesundheit zu sprechen, sollten Sie wissen, dass except:passSie durch die Verwendung kein Wissen und keine Kontrolle in Ihrem System haben. Als Faustregel gilt, im Fehlerfall eine Ausnahme auszulösen und entsprechende Maßnahmen zu ergreifen. Wenn Sie nicht im Voraus wissen, welche Aktionen dies sein sollten, protokollieren Sie den Fehler zumindest irgendwo (und lösen Sie die Ausnahme besser erneut aus):

try:
    something
except:
    logger.exception('Something happened')

Aber normalerweise machen Sie wahrscheinlich etwas falsch , wenn Sie versuchen, eine Ausnahme zu erwischen!


2
... sofern nicht ausdrücklich zum Schweigen gebracht, was bei OP der Fall ist.
Hyperboreus

Ich möchte Ihre Lösung wissen. In der Tat, wenn wirklich nichts getan werden muss, liste ich nur die Fehler in Ausnahme auf und mache Kommentare und schreibe Protokolle. Dann einfach vorbei.
Booster

2
@Hyperboreus, ich glaube nicht, dass das Abfangen aller und aller Fehler sie explizit zum Schweigen bringt, dh Sie wissen nicht einmal, was Sie abfangen.
Alexander Zhukov

13
"Weil jemand das sagt" ist nicht wirklich eine Antwort auf ein "Warum?" Frage.
Sebastian Negraszus

12

Das except:passKonstrukt bringt im Wesentlichen alle außergewöhnlichen Bedingungen zum Schweigen, die auftreten, während der im try:Block abgedeckte Code ausgeführt wird.

Was diese schlechte Praxis ausmacht, ist, dass es normalerweise nicht das ist, was Sie wirklich wollen. In den meisten Fällen tritt eine bestimmte Bedingung auf, die Sie zum Schweigen bringen möchten, und sie except:passist zu stumpf. Es wird die Arbeit erledigen, aber es wird auch andere Fehlerzustände maskieren, die Sie wahrscheinlich nicht erwartet haben, die Sie aber möglicherweise auf andere Weise behandeln möchten.

Was dies in Python besonders wichtig macht, ist, dass Ausnahmen nach den Redewendungen dieser Sprache nicht unbedingt Fehler sind . Sie werden natürlich oft so verwendet, genau wie in den meisten Sprachen. Insbesondere Python hat sie jedoch gelegentlich verwendet, um einen alternativen Exit-Pfad für einige Code-Tasks zu implementieren, der nicht wirklich Teil des normalen laufenden Falls ist, von dem jedoch bekannt ist, dass er von Zeit zu Zeit auftritt und in den meisten Fällen sogar erwartet werden kann. SystemExitwurde bereits als altes Beispiel erwähnt, aber das heutzutage häufigste Beispiel mag sein StopIteration. Die Verwendung von Ausnahmen auf diese Weise verursachte viele Kontroversen, insbesondere als Iteratoren und Generatoren zum ersten Mal in Python eingeführt wurden, aber schließlich setzte sich die Idee durch.


12

Der Hauptgrund wurde bereits angegeben - er verbirgt Fehler, die Sie nicht erwartet haben.

(# 2) - Es macht Ihren Code für andere schwer zu lesen und zu verstehen. Wenn Sie beim Versuch, eine Datei zu lesen, eine FileNotFoundException abfangen, ist es für einen anderen Entwickler ziemlich offensichtlich, welche Funktionalität der 'catch'-Block haben sollte. Wenn Sie keine Ausnahme angeben, benötigen Sie zusätzliche Kommentare, um zu erläutern, was der Block tun soll.

(# 3) - Es zeigt faul programmieren. Wenn Sie das generische try / catch verwenden, bedeutet dies, dass Sie entweder die möglichen Laufzeitfehler in Ihrem Programm nicht verstehen oder nicht wissen, welche Ausnahmen in Python möglich sind. Das Abfangen eines bestimmten Fehlers zeigt, dass Sie sowohl Ihr Programm als auch den von Python ausgelösten Fehlerbereich verstehen. Dies führt eher dazu, dass andere Entwickler und Code-Reviewer Ihrer Arbeit vertrauen.


12

Welche Ausgabe erzeugt dieser Code?

fruits = [ 'apple', 'pear', 'carrot', 'banana' ]

found = False
try:
     for i in range(len(fruit)):
         if fruits[i] == 'apple':
             found = true
except:
     pass

if found:
    print "Found an apple"
else:
    print "No apples in list"

Stellen Sie sich jetzt das vor try - exceptBlock besteht aus Hunderten von Aufrufzeilen an eine komplexe Objekthierarchie und wird selbst in der Mitte des Aufrufbaums eines großen Programms aufgerufen. Wenn das Programm schief geht, wo fangen Sie an zu suchen?


5
Ähm, danke an Leute, die dies "korrigiert" haben, aber bitte nicht - es ist absichtlich falsch im Sinne einer "Interviewfrage". Es ist möglicherweise subtiler, dass es zuerst erscheint - versuchen Sie es. Mein Punkt ist, dass das Quetschen von "allen" Ausnahmen, insbesondere in Python, das Debuggen selbst in einem trivialen Dutzend Codezeilen schwierig macht.
Ian Harvey

11

Im Allgemeinen können Sie jeden Fehler / jede Ausnahme in eine von drei Kategorien einteilen :

  • Tödlich : Nicht deine Schuld, du kannst sie nicht verhindern, du kannst dich nicht von ihnen erholen. Sie sollten sie auf keinen Fall ignorieren und fortfahren und Ihr Programm in einem unbekannten Zustand belassen. Lassen Sie einfach den Fehler Ihr Programm beenden, Sie können nichts tun.

  • Boneheaded : Ihre eigene Schuld, höchstwahrscheinlich aufgrund eines Versehens, eines Fehlers oder eines Programmierfehlers. Sie sollten den Fehler beheben. Auch hier sollten Sie mit Sicherheit nicht ignorieren und fortfahren.

  • Exogen : Sie können diese Fehler in Ausnahmesituationen erwarten, z. B. wenn die Datei nicht gefunden oder die Verbindung beendet wurde . Sie sollten diese Fehler explizit behandeln und nur diese.

In jedem Fall except: passwird Ihr Programm nur in einem unbekannten Zustand belassen, wo es mehr Schaden verursachen kann.


6

Einfach ausgedrückt, wenn eine Ausnahme oder ein Fehler ausgelöst wird, stimmt etwas nicht. Es mag nicht sehr falsch sein, aber das Erstellen, Werfen und Abfangen von Fehlern und Ausnahmen, nur um goto-Anweisungen zu verwenden, ist keine gute Idee und wird selten durchgeführt. In 99% der Fälle gab es irgendwo ein Problem.

Probleme müssen gelöst werden. Genau wie im Leben, beim Programmieren, wenn Sie Probleme einfach in Ruhe lassen und versuchen, sie zu ignorieren, verschwinden sie nicht oft von alleine. stattdessen werden sie größer und vermehren sich. Um zu verhindern, dass ein Problem auf Ihnen wächst und weiter unten erneut auftritt, müssen Sie entweder 1) es beseitigen und das Chaos anschließend beseitigen oder 2) es eindämmen und das Chaos anschließend beseitigen.

Nur Ausnahmen und Fehler zu ignorieren und sie so zu belassen, ist eine gute Möglichkeit, Speicherlecks, ausstehende Datenbankverbindungen, unnötige Sperren für Dateiberechtigungen usw. festzustellen.

In seltenen Fällen ist das Problem so winzig, trivial und - abgesehen von einem Versuch ... Fangblock - in sich geschlossen , dass es wirklich kein Chaos gibt , das danach beseitigt werden muss. Dies sind die einzigen Fälle, in denen diese bewährte Methode nicht unbedingt gilt. Nach meiner Erfahrung bedeutet dies im Allgemeinen, dass alles, was der Code tut, im Grunde genommen kleinlich und verzichtbar ist, und so etwas wie Wiederholungsversuche oder spezielle Nachrichten sind weder die Komplexität wert, noch den Thread aufzuhalten.

In meiner Firma ist die Regel, fast immer etwas in einem Fangblock zu tun , und wenn Sie nichts tun, müssen Sie immer einen Kommentar mit einem sehr guten Grund abgeben, warum nicht. Sie dürfen niemals einen leeren Fangblock passieren oder verlassen, wenn etwas zu tun ist.


6

Meiner Meinung nach haben Fehler einen Grund zu erscheinen, dass mein Sound dumm ist, aber so ist es nun mal. Eine gute Programmierung führt nur dann zu Fehlern, wenn Sie damit umgehen müssen. Wie ich vor einiger Zeit gelesen habe, ist "die pass-Anweisung eine Anweisung, die zeigt, dass Code später eingefügt wird". Wenn Sie also eine leere Ausnahme-Anweisung haben möchten, können Sie dies gerne tun, aber für ein gutes Programm wird dies der Fall sein ein Teil fehlt. weil Sie nicht mit den Dingen umgehen, die Sie haben sollten. Das Erscheinen von Ausnahmen gibt Ihnen die Möglichkeit, Eingabedaten zu korrigieren oder Ihre Datenstruktur zu ändern, damit diese Ausnahmen nicht erneut auftreten (in den meisten Fällen (Netzwerkausnahmen, allgemeine Eingabeausnahmen) weisen Ausnahmen darauf hin, dass die nächsten Teile des Programms nicht gut ausgeführt werden. Beispielsweise kann eine NetworkException eine unterbrochene Netzwerkverbindung anzeigen und das Programm kann in den nächsten Programmschritten keine Daten senden / empfangen.

Die Verwendung eines Passblocks für nur einen Ausführungsblock ist jedoch gültig, da Sie immer noch zwischen den Arten von Ausnahmen unterscheiden. Wenn Sie also alle Ausnahmeblöcke in einen setzen, ist dieser nicht leer:

try:
    #code here
except Error1:
    #exception handle1

except Error2:
    #exception handle2
#and so on

kann folgendermaßen umgeschrieben werden:

try:
    #code here
except BaseException as e:
    if isinstance(e, Error1):
        #exception handle1

    elif isinstance(e, Error2):
        #exception handle2

    ...

    else:
        raise

Selbst mehrere Ausnahmeblöcke mit Pass-Anweisungen können zu Code führen, dessen Struktur spezielle Arten von Ausnahmen behandelt.


4

Alle bisher vorgebrachten Kommentare sind gültig. Wenn möglich, müssen Sie genau angeben, welche Ausnahme Sie ignorieren möchten. Wenn möglich, müssen Sie analysieren, was eine Ausnahme verursacht hat, und nur ignorieren, was Sie ignorieren wollten, und nicht den Rest. Wenn eine Ausnahme dazu führt, dass die Anwendung "spektakulär abstürzt", dann ist es viel wichtiger zu wissen, dass das Unerwartete passiert ist, als zu verbergen, dass das Problem jemals aufgetreten ist.

Nehmen Sie trotz alledem keine Programmierpraxis als oberstes Gebot. Das ist blöd. Es gibt immer die Zeit und den Ort, um alle Ausnahmen zu ignorieren.

Ein weiteres Beispiel für idiotische Vorrangstellung ist die Verwendung des gotoOperators. Als ich in der Schule war, hat unser Professor uns den gotoBediener beigebracht, nur um zu erwähnen, dass du ihn NIEMALS benutzen sollst. Glauben Sie nicht, dass Leute Ihnen sagen, dass xyz niemals verwendet werden sollte, und es kann kein Szenario geben, in dem es nützlich ist. Das gibt es immer.


1
Der Fall "goto" ist stilistisch und Ansichtssache, während "without: pass" normalerweise sachlich falsch ist. Es wird davon ausgegangen, dass jemand, der beispielsweise Ihren Prozess zu diesem Zeitpunkt "-TERM" töten würde, diesen ignorieren sollte. Zumindest ist das schlechtes Benehmen.
Score_Under

1
@Score_Under gibt es noch Fälle, in denen dies angemessen ist. Wenn beispielsweise eine von Ihnen aufgerufene Funktion ergänzend ist und einen unbekannten Ursprung / Autor hat, wirkt sich dies nicht auf die Kernfunktionalität aus, aber wenn Abstürze Probleme verursachen können. Mir ist klar, dass Sie argumentieren würden, dass solche Anrufe ordnungsgemäß recherchiert und analysiert werden sollten, aber im wirklichen Leben ist dies nicht immer möglich.
Galets

Wenn ich Ihren Prozess jedoch beenden möchte, sollte kill -9 nicht die einzige zuverlässige Option sein.
Score_Under

2

Der Umgang mit Fehlern ist bei der Programmierung sehr wichtig. Sie müssen dem Benutzer zeigen, was schief gelaufen ist. In sehr wenigen Fällen können Sie die Fehler ignorieren. Dies ist eine sehr schlechte Programmierpraxis.


2

Da es noch nicht erwähnt wurde, ist es besser, es zu verwenden contextlib.suppress:

with suppress(FileNotFoundError):
    os.remove('somefile.tmp')

Beachten Sie, dass im angegebenen Beispiel der Programmstatus derselbe bleibt, unabhängig davon, ob die Ausnahme auftritt oder nicht. Das heißt, wird somefile.tmpimmer nicht mehr existent.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.