Einführung
Obwohl Ausnahmen früher eingeführt wurden, füllt dieser Abschnitt einige zusätzliche Details über die Fehlerprüfung und die Ausnahmebehandlung aus.
This tutorial is from open-source community. Access the source code
💡 Dieser Artikel wurde von AI-Assistenten übersetzt. Um die englische Version anzuzeigen, können Sie hier klicken
Obwohl Ausnahmen früher eingeführt wurden, füllt dieser Abschnitt einige zusätzliche Details über die Fehlerprüfung und die Ausnahmebehandlung aus.
Python führt keine Prüfung oder Validierung der Typen oder Werte von Funktionsargumenten durch. Eine Funktion funktioniert mit beliebigen Daten, die mit den Anweisungen in der Funktion kompatibel sind.
def add(x, y):
return x + y
add(3, 4) ## 7
add('Hello', 'World') ## 'HelloWorld'
add('3', '4') ## '34'
Wenn in einer Funktion Fehler auftreten, erscheinen sie zur Laufzeit (als Ausnahme).
def add(x, y):
return x + y
>>> add(3, '4')
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for +:
'int' and 'str'
>>>
Um den Code zu überprüfen, liegt ein starker Schwerpunkt auf der Tests (siehe später).
Ausnahmen werden verwendet, um Fehler zu signalisieren. Um selbst eine Ausnahme zu erhöhen, verwenden Sie die raise
-Anweisung.
if name not in authorized:
raise RuntimeError(f'{name} not authorized')
Um eine Ausnahme zu fangen, verwenden Sie try-except
.
try:
authenticate(username)
except RuntimeError as e:
print(e)
Ausnahmen breiten sich bis zu der ersten passenden except
aus.
def grok():
...
raise RuntimeError('Whoa!') ## Exception raised here
def spam():
grok() ## Call that will raise exception
def bar():
try:
spam()
except RuntimeError as e: ## Exception caught here
...
def foo():
try:
bar()
except RuntimeError as e: ## Exception does NOT arrive here
...
foo()
Um die Ausnahme zu behandeln, legen Sie Anweisungen im except
-Block fest. Sie können beliebige Anweisungen hinzufügen, um den Fehler zu behandeln.
def grok():...
raise RuntimeError('Whoa!')
def bar():
try:
grok()
except RuntimeError as e: ## Exception caught here
statements ## Use this statements
statements
...
bar()
Nach der Behandlung setzt die Ausführung mit der ersten Anweisung nach dem try-except
fort.
def grok():...
raise RuntimeError('Whoa!')
def bar():
try:
grok()
except RuntimeError as e: ## Exception caught here
statements
statements
...
statements ## Resumes execution here
statements ## And continues here
...
bar()
Es gibt etwa zwei Dutzend eingebaut Exceptions. Normalerweise ist der Name der Exception anzeigend für das, was schiefgeht (z.B. eine ValueError
wird ausgelöst, weil Sie einen schlechten Wert angegeben haben). Dies ist keine erschöpfende Liste. Überprüfen Sie die Dokumentation für mehr Informationen.
ArithmeticError
AssertionError
EnvironmentError
EOFError
ImportError
IndexError
KeyboardInterrupt
KeyError
MemoryError
NameError
ReferenceError
RuntimeError
SyntaxError
SystemError
TypeError
ValueError
Ausnahmen haben einen zugeordneten Wert. Er enthält genauere Informationen darüber, was schiefgeht.
raise RuntimeError('Invalid user name')
Dieser Wert ist Teil der Ausnahmeinstanz, die in die Variable eingefügt wird, die an except
übergeben wird.
try:
...
except RuntimeError as e: ## `e` hält die ausgelöste Ausnahme
...
e
ist eine Instanz des Ausnahmetyps. Wenn es aber gedruckt wird, sieht es oft wie eine Zeichenkette aus.
except RuntimeError as e:
print('Failed : Reason', e)
Sie können verschiedene Arten von Ausnahmen mit mehreren except
-Blöcken fangen.
try:
...
except LookupError as e:
...
except RuntimeError as e:
...
except IOError as e:
...
except KeyboardInterrupt as e:
...
Alternativ können Sie sie gruppieren, wenn die Anweisungen, um sie zu behandeln, dieselben sind:
try:
...
except (IOError,LookupError,RuntimeError) as e:
...
Um jede Ausnahme zu fangen, verwenden Sie Exception
wie folgt:
try:
...
except Exception: ## GEFÄHR. Siehe unten
print('An error occurred')
Im Allgemeinen ist es ein schlechter Gedanke, so etwas zu schreiben, da Sie nicht wissen, warum es fehlgeschlagen ist.
Hier ist die falsche Art, Ausnahmen zu verwenden.
try:
go_do_something()
except Exception:
print('Computer says no')
Dies fängt alle möglichen Fehler ab und kann es unmöglich machen, das Problem zu debuggen, wenn der Code aus einem Grund fehlschlägt, den Sie gar nicht erwartet haben (z.B. ein nicht installierter Python-Modul usw.).
Wenn Sie alle Fehler fangen möchten, ist dies ein vernünftigerer Ansatz.
try:
go_do_something()
except Exception as e:
print('Computer sagt nein. Grund :', e)
Es meldet einen spezifischen Grund für das Versagen. Es ist fast immer ein guter Gedanke, einen Mechanismus zur Anzeige/Berichterstattung von Fehlern zu haben, wenn Sie Code schreiben, der alle möglichen Ausnahmen fängt.
Im Allgemeinen ist es jedoch besser, die Fehler so eng wie vernünftig zu fangen. Fangen Sie nur die Fehler, die Sie tatsächlich behandeln können. Lassen Sie andere Fehler passieren - vielleicht kann ein anderer Code sie behandeln.
Verwenden Sie raise
, um einen gefangenen Fehler zu propagieren.
try:
go_do_something()
except Exception as e:
print('Computer sagt nein. Grund :', e)
raise
Dies ermöglicht es Ihnen, Maßnahmen zu ergreifen (z.B. Protokollierung) und den Fehler an den Aufrufer weiterzugeben.
Fang keine Ausnahmen. Scheitere schnell und lautstark. Wenn es wichtig ist, wird jemand anders das Problem beheben. Fang nur eine Ausnahme, wenn Sie der jemand sind. Das heißt, fangen Sie nur Fehler, bei denen Sie sich erholen und vernünftig weitermachen können.
finally
-AnweisungSie gibt an, welche Codezeilen unabhängig davon ausgeführt werden müssen, ob eine Ausnahme auftritt oder nicht.
lock = Lock()
...
lock.acquire()
try:
...
finally:
lock.release() ## Dies wird IMMER ausgeführt. Mit und ohne Ausnahme.
Wird häufig verwendet, um Ressourcen sicher zu verwalten (insbesondere Locks, Dateien usw.).
with
-AnweisungIn modernem Code wird try-finally
oft durch die with
-Anweisung ersetzt.
lock = Lock()
with lock:
## lock acquired
...
## lock released
Ein vertrauteres Beispiel:
with open(filename) as f:
## Verwenden Sie die Datei
...
## Datei geschlossen
with
definiert einen Verwendungskontext für eine Ressource. Wenn die Ausführung diesen Kontext verlässt, werden die Ressourcen freigegeben. with
funktioniert nur mit bestimmten Objekten, die speziell programmiert wurden, um dies zu unterstützen.
Die parse_csv()
-Funktion, die Sie im letzten Abschnitt geschrieben haben, ermöglicht die Auswahl von benutzerdefinierten Spalten, aber das funktioniert nur, wenn die Eingabedatei Spaltenüberschriften hat.
Ändern Sie den Code so, dass eine Ausnahme ausgelöst wird, wenn sowohl die Argumente select
und has_headers=False
übergeben werden. Beispielsweise:
>>> parse_csv('/home/labex/project/prices.csv', select=['name','price'], has_headers=False)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "fileparse.py", line 9, in parse_csv
raise RuntimeError("select argument requires column headers")
RuntimeError: select argument requires column headers
>>>
Nachdem Sie diese eine Prüfung hinzugefügt haben, könnten Sie fragen, ob Sie in der Funktion andere Arten von Konsistenzprüfungen durchführen sollten. Beispielsweise sollten Sie überprüfen, ob der Dateiname ein String ist, ob types
eine Liste ist oder etwas dergleichen?
Im Allgemeinen ist es normalerweise am besten, solche Tests zu überspringen und einfach zuzulassen, dass das Programm bei falschen Eingaben fehlschlägt. Die Fehlermeldung in der Stapelüberwachung wird auf die Quelle des Problems verweisen und kann beim Debuggen helfen.
Der Hauptgrund für das Hinzufügen der obigen Prüfung ist es, das Ausführen des Codes in einem unsinnigen Modus zu vermeiden (z.B. das Verwenden eines Features, das Spaltenüberschriften erfordert, aber gleichzeitig angibt, dass es keine Überschriften gibt).
Dies deutet auf einen Programmierfehler in dem aufrufenden Code hin. Das Überprüfen von Fällen, die "nicht passieren sollten", ist oft eine gute Idee.
Die von Ihnen geschriebene parse_csv()
-Funktion wird verwendet, um den gesamten Inhalt einer Datei zu verarbeiten. In der Realität ist es jedoch möglich, dass Eingabedateien beschädigte, fehlende oder unsaubere Daten enthalten. Versuchen Sie dieses Experiment:
>>> portfolio = parse_csv('missing.csv', types=[str, int, float])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "fileparse.py", line 36, in parse_csv
row = [func(val) for func, val in zip(types, row)]
ValueError: invalid literal for int() with base 10: ''
>>>
Ändern Sie die parse_csv()
-Funktion, um alle ValueError
-Ausnahmen, die während der Erstellung von Datensätzen generiert werden, zu fangen, und geben Sie eine Warnmeldung für Zeilen aus, die nicht konvertiert werden können.
Die Meldung sollte die Zeilennummer und Informationen über den Grund enthalten, warum der Konvertierungsversuch fehlschlug. Um Ihre Funktion zu testen, versuchen Sie, die obige Datei missing.csv
zu lesen. Beispielsweise:
>>> portfolio = parse_csv('missing.csv', types=[str, int, float])
Zeile 4: Konvertierung von ['MSFT', '', '51.23'] nicht möglich
Zeile 4: Grund: invalid literal for int() with base 10: ''
Zeile 7: Konvertierung von ['IBM', '', '70.44'] nicht möglich
Zeile 7: Grund: invalid literal for int() with base 10: ''
>>>
>>> portfolio
[{'name': 'AA','shares': 100, 'price': 32.2}, {'name': 'IBM','shares': 50, 'price': 91.1}, {'name': 'CAT','shares': 150, 'price': 83.44}, {'name': 'GE','shares': 95, 'price': 40.37}, {'name': 'MSFT','shares': 50, 'price': 65.1}]
>>>
Ändern Sie die parse_csv()
-Funktion so, dass Fehler bei der Analyse unterdrückt werden können, wenn der Benutzer dies explizit wünscht. Beispielsweise:
>>> portfolio = parse_csv('missing.csv', types=[str,int,float], silence_errors=True)
>>> portfolio
[{'name': 'AA','shares': 100, 'price': 32.2}, {'name': 'IBM','shares': 50, 'price': 91.1}, {'name': 'CAT','shares': 150, 'price': 83.44}, {'name': 'GE','shares': 95, 'price': 40.37}, {'name': 'MSFT','shares': 50, 'price': 65.1}]
>>>
Das Fehlerhandeln ist eine der schwierigsten Dinge, die man in den meisten Programmen richtig machen muss. Im Allgemeinen sollten Sie Fehler nicht stillschweigend ignorieren. Stattdessen ist es besser, Probleme zu melden und dem Benutzer die Möglichkeit zu geben, die Fehlermeldung zu unterdrücken, wenn er dies wählt.
Herzlichen Glückwunsch! Sie haben das Lab zu Fehlerprüfung abgeschlossen. Sie können in LabEx weitere Labs absolvieren, um Ihre Fähigkeiten zu verbessern.