Ustawienie
Często mam problemy z określeniem, kiedy i jak korzystać z wyjątków. Rozważmy prosty przykład: załóżmy, że przeglądam stronę internetową, powiedz „ http://www.abevigoda.com/ ”, aby ustalić, czy Abe Vigoda nadal żyje. Aby to zrobić, wystarczy pobrać stronę i poszukać czasów, w których pojawia się zwrot „Abe Vigoda”. Zwracamy pierwszy występ, ponieważ obejmuje to status Abe. Koncepcyjnie będzie to wyglądać tak:
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"
Gdzie parse_abe_status(s)przyjmuje ciąg formy „Abe Vigoda jest czymś ” i zwraca część „ coś ”.
Zanim przekonasz się, że istnieją znacznie lepsze i bardziej niezawodne sposoby skrobania tej strony w celu uzyskania statusu Abe, pamiętaj, że jest to prosty i przemyślany przykład użyty do podkreślenia typowej sytuacji, w której się znajduję.
Gdzie ten kod może napotykać problemy? Wśród innych błędów niektóre „oczekiwane” to:
download_pagemoże nie być w stanie pobrać strony i zgłaszaIOError.- Adres URL może nie wskazywać właściwej strony lub strona jest niepoprawnie pobrana, więc nie ma żadnych trafień.
hitsjest więc pusta lista. - Strona internetowa została zmieniona, co prawdopodobnie czyni nasze założenia dotyczące strony błędnymi. Może oczekujemy 4 wzmianek o Abe Vigodzie, ale teraz znajdujemy 5.
- Z niektórych powodów
hits[0]może nie być ciągiem w formie „Abe Vigoda jest czymś ”, więc nie można go poprawnie przeanalizować.
Pierwszy przypadek nie jest dla mnie problemem: an IOErrorjest rzucany i może być obsłużony przez program wywołujący moją funkcję. Zastanówmy się więc nad innymi przypadkami i jak sobie z nimi poradzić. Ale najpierw załóżmy, że wdrażamy parse_abe_statusw najgłupszy możliwy sposób:
def parse_abe_status(s):
return s[13:]
Mianowicie nie sprawdza błędów. Teraz przejdź do opcji:
Opcja 1: powrót None
Mogę powiedzieć dzwoniącemu, że coś poszło nie tak, zwracając 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"
Jeśli dzwoniący otrzymuje Noneod mojej funkcji, powinien założyć, że nie było wzmianek o Abe Vigodzie, a więc coś poszło nie tak. Ale to dość niejasne, prawda? I to nie pomaga w przypadku, gdy hits[0]nie jest tak, jak nam się wydawało.
Z drugiej strony możemy wprowadzić pewne wyjątki:
Opcja 2: Korzystanie z wyjątków
Jeśli hitsjest pusty, IndexErrorzostanie rzucony podczas próby hits[0]. Ale nie należy oczekiwać, że osoba dzwoniąca poradzi sobie z IndexErrorrzuconą przez moją funkcję, ponieważ nie ma pojęcia, skąd ona IndexErrorpochodzi; mogło to zostać zrzucone find_all_mentions, o ile on wie. Dlatego stworzymy niestandardową klasę wyjątków, aby obsłużyć to:
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"
Co się stanie, jeśli strona ulegnie zmianie i pojawi się nieoczekiwana liczba wyświetleń? Nie jest to katastrofalne, ponieważ kod może nadal działać, ale osoba dzwoniąca może chcieć być bardzo ostrożna lub może zarejestrować ostrzeżenie. Więc rzucę ostrzeżenie:
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"
Wreszcie możemy odkryć, że statusnie jest ani żywy, ani martwy. Być może z jakiegoś dziwnego powodu dziś tak się stało comatose. Więc nie chcę wracać False, ponieważ to sugeruje, że Abe nie żyje. Co mam tu zrobić? Prawdopodobnie rzuć wyjątek. Ale jaki? Czy powinienem utworzyć niestandardową klasę wyjątków?
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"
Opcja 3: Gdzieś pomiędzy
Myślę, że druga metoda, z wyjątkami, jest lepsza, ale nie jestem pewien, czy prawidłowo używam wyjątków. Jestem ciekawy, jak poradzą sobie z tym bardziej doświadczeni programiści.