Menu

Wyjątki w Pythonie: try, except, else, finally i raise

Jak obsługiwać błędy w Pythonie: try/except/finally, łapanie konkretnych wyjątków, zgłaszanie własnych i kiedy pozwolić błędowi się propagować.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Błędy to po prostu wartości ze złym charakterem

Gdy w Pythonie coś idzie nie tak (dzielenie przez zero, odczyt brakującego pliku, parsowanie złej liczby), środowisko tworzy obiekt wyjątku i zaczyna zwijać stos wywołań, aż coś go złapie. Jeśli nic go nie złapie, program kończy działanie i wypisuje traceback.

Wyjątki same w sobie nie są złe. To sposób, w jaki Python sygnalizuje "nie mogę kontynuować tej operacji, a oto dlaczego". Twoim zadaniem jest decydować w każdym przypadku osobno, po których umiesz się pozbierać, a które należy przepuścić wyżej.

Podstawowy kształt

  • try: otwiera blok ryzykownego kodu.
  • except ValueError: łapie ten konkretny wyjątek, jeśli zostanie zgłoszony w try.
  • Jeśli żaden wyjątek nie wystąpi, except jest całkowicie pomijany.

Uruchom fragment z 42: działa. Uruchom go z hello: wykonuje się obsługa błędu.

Łapanie konkretnych wyjątków

Python ma hierarchię typów wyjątków. Kilka, które spotkasz często:

  • ValueError: wartość jest w jakiś sposób niepoprawna (int("abc"), argumenty spoza zakresu).
  • TypeError: użyto złego typu ("hi" + 3).
  • KeyError: nie znaleziono klucza w słowniku.
  • IndexError: indeks sekwencji jest poza zakresem.
  • FileNotFoundError: plik nie istnieje.
  • ZeroDivisionError: próba dzielenia przez zero.
  • AttributeError: obiekt nie ma żądanego atrybutu.

Łap ten konkretny, który umiesz obsłużyć:

Kilka wyjątków możesz złapać w jednej klauzuli, przekazując krotkę:

Zwróć uwagę na as e. To wiąże obiekt wyjątku z e, żeby można było sprawdzić jego komunikat albo atrybuty.

Nie łap wszystkiego

Gołe except: łapie dosłownie wszystko, łącznie z KeyboardInterrupt (twoim Ctrl-C) i wyjściem na poziomie systemu. Nie używaj go.

except Exception: jest trochę lepsze, ale nadal niebezpieczne: połyka nieprzewidziane błędy i ukrywa prawdziwe źródło problemów:

# Nie rób tego bez naprawdę dobrego powodu.
try:
    do_something()
except Exception:
    pass

Właściwy ruch to prawie zawsze łapanie konkretnego wyjątku, po którym umiesz się pozbierać. Jeśli nieoczekiwany wyjątek dotrze na szczyt programu, traceback powie ci dokładnie, co poszło nie tak, i to jest zaleta, a nie wada.

else i finally

Instrukcja try ma jeszcze dwie opcjonalne klauzule:

  • else wykonuje się, jeśli blok try zakończył się bez wyjątku.
  • finally wykonuje się zawsze, niezależnie od tego, czy był wyjątek.

else to najczystsze miejsce na kod "tylko przy sukcesie": blok try nie powinien robić więcej niż ta część, która naprawdę może zawieść. finally służy do sprzątania, które musi się wykonać nawet przy niepowodzeniu: zamknięcia zasobu, zwolnienia blokady, przywrócenia stanu.

Samodzielne zgłaszanie wyjątków

Użyj raise, by zasygnalizować błąd we własnym kodzie:

Wybierz typ wyjątku pasujący do tego, co poszło źle. Najpierw sięgaj po wbudowane (ValueError, TypeError, FileNotFoundError), zanim zdefiniujesz własną klasę.

Definiowanie własnego wyjątku

Gdy wbudowane wyjątki nie oddają znaczenia, zdefiniuj własny:

Dziedzicz po Exception (albo po bardziej konkretnym wbudowanym wyjątku) i dodaj klasie docstring. Zwykle tyle wystarczy. Własne wyjątki pozwalają wywołującym łapać tylko ten błąd, który ma znaczenie w ich dziedzinie.

raise ... from ...: łańcuchy wyjątków

Gdy jeden wyjątek wywołuje drugi, zachowaj łańcuch:

from e dołącza oryginalny błąd. Gdy traceback jest wypisywany, Python pokazuje oba: ConfigError, który wypłynął na wierzch, i FileNotFoundError, który go spowodował. Taki ślad jest bezcenny przy debugowaniu.

Menedżery kontekstu: czystszy sposób na sprzątanie

finally jest w porządku, ale dla zasobów takich jak pliki menedżer kontekstu (to, czego używa with) jest prawie zawsze lepszy:

# wersja z finally
f = open("data.txt")
try:
    data = f.read()
finally:
    f.close()

# wersja z with
with open("data.txt") as f:
    data = f.read()

Obie są bezpieczne. Forma z with jest krótsza i działa automatycznie. Po finally sięgaj tylko wtedy, gdy robisz coś, czego biblioteka standardowa nie opakowuje już w menedżer kontekstu.

Kiedy nie łapać

Złapanie wyjątku to decyzja: mówisz "umiem to obsłużyć". Jeśli nie umiesz, pozwól wyjątkowi się propagować. Taki kod to prawie zawsze błąd:

try:
    do_work()
except Exception:
    pass  # po cichu ignoruje wszystko

Uciszanie błędów sprawia, że stają się niewidoczne. Lepiej wyłożyć się głośno, niż kuśtykać dalej w niespójnym stanie.

Podsumowanie

  • try/except pozwala obsłużyć błędy, po których da się pozbierać.
  • Łap konkretne wyjątki, a nie Exception na ślepo.
  • raise sygnalizuje błędy we własnym kodzie.
  • Bloki with zastępują większość sprzątania w finally.
  • W razie wątpliwości pozwól wyjątkowi się propagować.

Dalej: przegląd konkretnych błędów, które Python zgłasza najczęściej (KeyError, ValueError, ModuleNotFoundError i kilka innych), oraz nawyków debugowania, które pozwalają szybko je naprawić.

Najczęściej zadawane pytania

Jak obsługiwać błędy w Pythonie?

Umieść ryzykowny kod w bloku try, a konkretny wyjątek złap w bloku except: try: risky() except ValueError: .... Opcjonalne else wykonuje się, gdy nie wystąpił żaden wyjątek, a finally wykonuje się zawsze i służy do sprzątania.

Czy dla bezpieczeństwa łapać Exception?

Nie. Gołe except: albo except Exception: ukrywa błędy, których nie przewidziano. Łap konkretny wyjątek, po którym umiesz się pozbierać, a całą resztę przepuszczaj dalej, żeby widzieć prawdziwy problem.

Czym różni się raise od raise from?

raise NewError(...) zgłasza nowy wyjątek. raise NewError(...) from original zachowuje oryginalny wyjątek jako przyczynę, którą Python pokazuje w tracebacku. Używaj from, gdy błąd niskiego poziomu wywołał błąd wyższego poziomu, który chcesz pokazać.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ