Die Einstellung
Ich habe oft Probleme festzustellen, wann und wie ich Ausnahmen verwenden soll. Betrachten wir ein einfaches Beispiel: Angenommen, ich schabe eine Webseite mit der Aufschrift " http://www.abevigoda.com/ ", um festzustellen, ob Abe Vigoda noch am Leben ist. Dazu müssen wir nur die Seite herunterladen und nach Zeiten suchen, in denen der Ausdruck "Abe Vigoda" erscheint. Wir geben den ersten Auftritt zurück, da dies Abes Status einschließt. Konzeptionell sieht es so aus:
def get_abe_status(url):
# download the page
page = download_page(url)
# get all mentions of Abe Vigoda
hits = page.find_all_mentions("Abe Vigoda")
# parse the first hit for his status
status = parse_abe_status(hits[0])
# he's either alive or dead
return status == "alive"
Wobei parse_abe_status(s)
eine Zeichenkette der Form "Abe Vigoda ist etwas " annimmt und den Teil " etwas " zurückgibt .
Bevor Sie argumentieren, dass es viel bessere und robustere Möglichkeiten gibt, diese Seite nach Abes Status zu durchsuchen, denken Sie daran, dass dies nur ein einfaches und ausgeklügeltes Beispiel ist, das verwendet wird, um eine häufige Situation hervorzuheben, in der ich mich befinde.
Wo kann dieser Code auf Probleme stoßen? Einige "erwartete" Fehler sind unter anderem:
download_page
ist möglicherweise nicht in der Lage, die Seite herunterzuladen, und wirft eineIOError
.- Die URL verweist möglicherweise nicht auf die richtige Seite, oder die Seite wurde falsch heruntergeladen, sodass es keine Treffer gibt.
hits
ist also die leere Liste. - Die Webseite wurde geändert, wodurch möglicherweise unsere Vermutungen über die Seite falsch sind. Vielleicht erwarten wir 4 Erwähnungen von Abe Vigoda, aber jetzt finden wir 5.
- Aus bestimmten Gründen ist
hits[0]
möglicherweise keine Zeichenfolge der Form "Abe Vigoda ist etwas " vorhanden und kann daher nicht korrekt analysiert werden.
Der erste Fall ist für mich eigentlich kein Problem: Ein IOError
wird geworfen und kann vom Aufrufer meiner Funktion bearbeitet werden. Betrachten wir also die anderen Fälle und wie ich damit umgehen könnte. parse_abe_status
Nehmen wir aber zunächst an, wir implementieren auf die dümmste Art und Weise:
def parse_abe_status(s):
return s[13:]
Es wird nämlich keine Fehlerprüfung durchgeführt. Nun zu den Optionen:
Option 1: Rückkehr None
Ich kann dem Anrufer sagen, dass etwas schief gelaufen ist, indem ich zurückkehre None
:
def get_abe_status(url):
# download the page
page = download_page(url)
# get all mentions of Abe Vigoda
hits = page.find_all_mentions("Abe Vigoda")
if not hits:
return None
# parse the first hit for his status
status = parse_abe_status(hits[0])
# he's either alive or dead
return status == "alive"
Wenn der Anrufer None
von meiner Funktion erhält , sollte er davon ausgehen, dass Abe Vigoda nicht erwähnt wurde, sodass ein Fehler aufgetreten ist. Aber das ist ziemlich vage, oder? Und es hilft nichts, wenn hits[0]
es nicht so ist, wie wir es uns vorgestellt haben.
Auf der anderen Seite können wir einige Ausnahmen machen:
Option 2: Verwenden von Ausnahmen
Wenn hits
leer, IndexError
wird beim Versuch ein geworfen hits[0]
. Es ist jedoch nicht zu erwarten, dass der Anrufer eine IndexError
von meiner Funktion ausgelöste Aufgabe übernimmt, da er keine Ahnung hat, woher diese IndexError
stammt. es hätte vorbei geworfen werden können find_all_mentions
, soweit er weiß. Deshalb erstellen wir eine benutzerdefinierte Ausnahmeklasse, um dies zu handhaben:
class NotFoundError(Exception):
"""Throw this when something can't be found on a page."""
def get_abe_status(url):
# download the page
page = download_page(url)
# get all mentions of Abe Vigoda
hits = page.find_all_mentions("Abe Vigoda")
try:
hits[0]
except IndexError:
raise NotFoundError("No mentions found.")
# parse the first hit for his status
status = parse_abe_status(hits[0])
# he's either alive or dead
return status == "alive"
Was ist nun, wenn sich die Seite geändert hat und unerwartet viele Treffer zu verzeichnen sind? Dies ist keine Katastrophe, da der Code möglicherweise immer noch funktioniert, ein Anrufer jedoch besonders vorsichtig sein oder eine Warnung protokollieren möchte. Also werfe ich eine Warnung:
class NotFoundError(Exception):
"""Throw this when something can't be found on a page."""
def get_abe_status(url):
# download the page
page = download_page(url)
# get all mentions of Abe Vigoda
hits = page.find_all_mentions("Abe Vigoda")
try:
hits[0]
except IndexError:
raise NotFoundError("No mentions found.")
# say we expect four hits...
if len(hits) != 4:
raise Warning("An unexpected number of hits.")
logger.warning("An unexpected number of hits.")
# parse the first hit for his status
status = parse_abe_status(hits[0])
# he's either alive or dead
return status == "alive"
Schließlich stellen wir möglicherweise fest, dass status
das weder lebend noch tot ist. Vielleicht stellte sich aus irgendeinem Grund heraus, dass es heute so war comatose
. Dann möchte ich nicht zurückkehren False
, da dies impliziert, dass Abe tot ist. Was soll ich hier machen? Wirf wahrscheinlich eine Ausnahme. Aber welche? Soll ich eine benutzerdefinierte Ausnahmeklasse erstellen?
class NotFoundError(Exception):
"""Throw this when something can't be found on a page."""
def get_abe_status(url):
# download the page
page = download_page(url)
# get all mentions of Abe Vigoda
hits = page.find_all_mentions("Abe Vigoda")
try:
hits[0]
except IndexError:
raise NotFoundError("No mentions found.")
# say we expect four hits...
if len(hits) != 4:
raise Warning("An unexpected number of hits.")
logger.warning("An unexpected number of hits.")
# parse the first hit for his status
status = parse_abe_status(hits[0])
if status not in ['alive', 'dead']:
raise SomeTypeOfError("Status is an unexpected value.")
# he's either alive or dead
return status == "alive"
Option 3: Irgendwo dazwischen
Ich denke, dass die zweite Methode mit Ausnahmen vorzuziehen ist, aber ich bin nicht sicher, ob ich Ausnahmen richtig darin verwende. Ich bin gespannt, wie erfahrene Programmierer damit umgehen würden.