2.25. Obsługa błędów

Większość problemów wykonania w Pythonie ujawnia się jako wyjątki – nazwany, ustrukturyzowany sposób zgłaszania, że coś poszło nie tak. ValueError, TypeError, KeyError, OSError, MemoryError to wszystko przykłady; każdy z nich jest klasą, a zgłoszenie jednego z nich zatrzymuje bieżące wywołanie i szuka procedury obsługi w otaczającym kodzie.

2.25.1. try / except

Otocz blok kodu instrukcją try, aby przechwycić każdy zgłoszony przez niego wyjątek:

try:
    n = int(input_text)
except ValueError:
    n = 0

Jeśli konwersja int(...) się nie powiedzie, sterowanie przeskakuje do bloku except zamiast propagować błąd dalej. Jeśli input_text był poprawnym łańcuchem liczby całkowitej, blok except jest pomijany.

Pojedynczy try może mieć kilka bloków except, z których każdy przechwytuje inny rodzaj błędu:

try:
    value = data[key]
except KeyError:
    value = None
except TypeError:
    value = -1

Python dopasowuje je po kolei; problem obsługuje pierwszy, którego klasa wyjątku pasuje. Przechwytywanie Exception (klasy bazowej dla niemal wszystkiego) obsługuje każdy błąd; zarezerwuj to dla najbardziej zewnętrznej warstwy programu, gdzie alternatywą jest awaria.

Ostrzeżenie

Goły except: (bez klasy po słowie kluczowym) przechwytuje także KeyboardInterrupt – wyjątek wysyłany przez IDE, gdy naciśniesz przycisk stop, aby przerwać działający skrypt. Pętla otoczona gołym except: pass połknie przerwanie i będzie działać dalej, nie pozostawiając sposobu na zatrzymanie skryptu poza wyłączeniem i włączeniem zasilania.

Gdy naprawdę musisz przechwytywać szeroko, preferuj except Exception: zamiast gołego except:. KeyboardInterrupt dziedziczy po BaseException, a nie po Exception, więc except Exception: pozostawia działający przycisk stop.

2.25.1.1. Badanie wyjątku

Aby odczytać komunikat dołączony do wyjątku, nazwij go za pomocą as:

try:
    f = open("data.txt")
except OSError as e:
    print("could not open file:", e)

Zmienna powiązana przez as jest ważna tylko wewnątrz bloku except.

2.25.2. else i finally

Blok try ma dwa opcjonalne dodatki.

else wykonuje się tylko wtedy, gdy try zakończył się bez zgłoszenia wyjątku:

try:
    value = compute()
except ValueError:
    print("bad input")
else:
    print("got", value)

Umieszczenie „co zrobić, gdy się powiodło” w else utrzymuje blok try wąskim – do try należy tylko wiersz, który może zawieść.

finally wykonuje się na końcu bez względu na wszystko – niezależnie od tego, czy try się powiódł, zgłosił wyjątek i został on obsłużony, czy też zgłosił go i ma się on właśnie propagować:

try:
    do_work()
finally:
    cleanup()
Diagram przepływu pokazujący cztery możliwe ścieżki przez try / except / else / finally: sukces prowadzi do else, a następnie finally; przechwycony wyjątek prowadzi do except, a następnie finally; nieprzechwycony wyjątek prowadzi do finally, a następnie się propaguje.

finally wykonuje się zawsze. else wykonuje się tylko na ścieżce bez wyjątku.

W przypadku większości wzorców pozyskania/zwolnienia zasobu preferuj menedżer kontekstu zamiast pary try / finally – sam zasób zarządza wtedy swoim sprzątaniem.

2.25.3. Częste wyjątki wbudowane

Krótka lista wyjątków, na które będziesz często trafiać:

  • ValueError – właściwy typ, błędna wartość (bytes([300]) – 300 jest właściwego typu, ale leży poza poprawnym zakresem bajtu 0..255).

  • TypeError – całkowicie błędny typ (len(42)).

  • KeyError – brakujący klucz w dict.

  • IndexError – indeks poza końcem sekwencji.

  • AttributeError – dostęp do nieistniejącego atrybutu ("abc".foo).

  • OSError – awaria systemu plików lub wejścia/wyjścia.

  • MemoryError – zabrakło sterty. W środowisku uruchomieniowym o ograniczonej pamięci może się to zdarzyć podczas normalnej pracy – nie tylko w skrajnych przypadkach.