programowanieBackend developer

Opisz mechanizmy obsługi wyjątków w Pythonie. Jak działa blok try/except/finally, do czego potrzebny jest raise i jakie pułapki często występują?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź

W Pythonie obsługa wyjątków jest realizowana przez konstrukcje try/except/else/finally oraz operator raise.

try definiuje chroniony blok kodu. Jeśli w jego wnętrzu wystąpi wyjątek, sterowanie przekazywane jest do najbliższego pasującego bloku except. Jeśli wyjątku nie ma — wykonywany jest blok else, jeśli taki istnieje. Blok finally jest wykonywany zawsze — niezależnie od tego, czy wystąpił wyjątek, czy nie (na przykład, aby zwolnić zasoby).

Operator raise wywołuje wyjątek jawnie lub powtarza bieżący (w bloku except bez wskazania typu).

Przykład:

try: x = 1 / 0 except ZeroDivisionError as e: print(f"Błąd: {e}") else: print("Nie było błędów") finally: print("Zawsze wykonywane")

Wynik:

Błąd: division by zero
Zawsze wykonywane

Szczegóły:

  • Przy zagnieżdżonych traktoriach ważna jest kolejność except: od typów szczegółowych do ogólnych (w przeciwnym razie szczegółowe nie będą obsługiwane).
  • Blok finally nie może zapobiec ponownemu rzuceniu wyjątku (jeśli wewnątrz finally wstawisz raise — aktualny będzie „najświeższy” błąd).

Pytanie z podstępem

Pytanie: „Co się stanie, jeśli w bloku try i finally wywołasz raise różne wyjątki?”

Odpowiedź: Wyjątek z finally „przykryje” wyjątek z try: na zewnątrz wyjdzie to, co wystrzeliło w finally.

def foo(): try: raise ValueError("w try") finally: raise IndexError("w finally") try: foo() except Exception as e: print(repr(e)) # Wyświetli: IndexError('w finally')

Przykłady rzeczywistych błędów z powodu nieznajomości szczegółów tematu


Historia

W procesie ETL w bloku finally bezwarunkowo zamykano połączenie z bazą, ale zapominali, że przy tym w finally może wystąpić wyjątek (na przykład, jeśli połączenie było już zamknięte). Wynik — „ukryty” wyjątek z finally całkowicie pochłaniał wyjątek z głównego kodu, znacznie utrudniając debugowanie.


Historia

Używano łańcuchów kilku except: od ogólnego except Exception nad szczegółowymi. W efekcie wszystkie specyficzne excepty zostawały „martwe” — wyjątki na niskim poziomie nie były łapane osobno, co utrudniało obsługę specyficznych błędów.


Historia

W serwisie internetowym w bloku except zapominali przekazać wyjątek dalej przez „raise”, logując błąd, ale pozwalali na dalsze wykonywanie. W efekcie rzeczywiste błędy „gubiły się”, a program kontynuował działanie w nieprawidłowym stanie.