Dateien lesen und schreiben · Übung
Bei Dateifehlern einen nächsten Schritt anbieten
read_books funktioniert, wenn seine Vereinbarung erfüllt ist: Der Pfad existiert, benennt eine lesbare Datei und enthält UTF-8-Text. An Schnittstellen zur Außenwelt wird diese Vereinbarung manchmal verletzt.
Zwei Fehler sind erwartbar genug, um hier Hilfestellung zu geben:
FileNotFoundError: An dem Pfad, den Python öffnen wollte, existiert nichts;UnicodeDecodeError: Es sind Bytes vorhanden, aber sie bilden keinen gültigen UTF-8-Text.
Keiner der beiden Fälle bedeutet, dass read_books eine leere Liste zurückgeben sollte. Eine leere Datei ist gültig und enthält tatsächlich keine Titel. Eine fehlende oder nicht dekodierbare Datei ist ein anderer Zustand. Ihn als „null Bücher“ zu tarnen würde eine glaubhafte Falschaussage erzeugen.
Die wiederverwendbare Funktion soll ehrlich bleiben
Lass read_books(path) diese Ausnahmen auslösen. Die Funktion hat keine Benutzeroberfläche und kann nicht wissen, ob ihr Aufrufer ein Kommandozeilenprogramm, ein späterer Datenkonverter oder ein Test ist. Einen besonderen Satz aus einer Funktion zurückzugeben, die Listen liefert, würde zwei Aufgaben vermischen.
Fange die Fehler in main() ab. Dort hat das Programm endlich genug Kontext, um einer Person zu sagen, was sie tun soll:
try:
books = read_books(BOOKS_PATH)
except FileNotFoundError:
print(f"Could not find {BOOKS_PATH}. Run this program from the project root.")
return
except UnicodeDecodeError:
print(f"Could not read {BOOKS_PATH} as UTF-8 text.")
return
Gib nach den beiden Fehlerbehandlungen Loaded 4 books. mit der tatsächlichen Länge aus. Spätere Lektionen ersetzen diese vorübergehende Erfolgsmeldung durch dauerhaft gespeicherte Ausgabe.
Nur abfangen, was du sinnvoll behandeln kannst
except Exception: würde auch einen falsch geschriebenen Variablennamen, eine fehlerhafte Berechnung oder einen morgen eingebauten Fehler abfangen. Das Programm könnte „file problem“ ausgeben, obwohl die Datei völlig in Ordnung ist. Dadurch verschwindet der ursprüngliche Traceback genau dann, wenn er am nützlichsten wäre.
Eng begrenzte Fehlerbehandlung soll nicht dafür sorgen, dass ein Programm niemals scheitert. Sie soll bekannte Fehler verständlicher machen und unbekannte Fehler sichtbar lassen.
Der Fehlertext sollte BOOKS_PATH enthalten. „File not found“ benennt eine Kategorie. „Could not find data/books.txt; run from the project root“ nennt einen nächsten Schritt und knüpft an die Pfadregel aus der ersten Lektion an.
Warum sollte eine fehlende Datei nicht zu einer leeren Liste werden?
Implementiere die beiden eng begrenzten Fehlerbehandlungen und die vorübergehende Erfolgsmeldung.
Prüfe anschließend, ob du nicht zu viel abfängst. Schreibe vorübergehend einen Variablennamen in read_books falsch, sodass ein NameError entsteht, und führe das Programm aus. Du möchtest einen Traceback sehen. Wenn stattdessen eine deiner beiden freundlichen Dateimeldungen erscheint, fängt die Fehlerbehandlung mehr ab, als sie versteht. Stelle den Namen anschließend wieder richtig.
Wenn du unsicher bist, ob eine Fehlerbehandlung zu weit reicht, frage Monty: „Was könnte except Exception hier abfangen, das ich lieber als Traceback sehen möchte?“ Die Liste ist meist länger, als man erwartet.
Aufgabe
Aktualisiere main() so, dass es:
read_books(BOOKS_PATH)aufruft;FileNotFoundErrorabfängt, eine Meldung mitBOOKS_PATHund dem nächsten Schritt zum Projektstamm ausgibt und dann zurückkehrt;UnicodeDecodeErrorabfängt, eine Meldung mitBOOKS_PATHundUTF-8ausgibt und dann zurückkehrt;andernfalls
Loaded N books.mit der tatsächlichen Listenlänge ausgibt.
Fange weder Exception noch andere unbeteiligte Programmierfehler ab.