https://docs.python.org/3/library/exceptions.html
https://docs.python.org/3/reference/executionmodel.html#exceptions
PEP 765 - Disallow return/break/continue that exit a finally block
Przy próbie uruchomienia kodu Pythona mogą pojawić się co najmniej dwa rodzaje błędów: błędy składniowe (syntax errors) oraz błędy czasu wykonania (runtime errors). Oba rodzaje błędów są obsługiwane przez wyjątki (exceptions). Z drugiej strony, pojawienie się wyjątku nie zawsze oznacza błąd w programie.
Błędy składniowe, inaczej błędy parsowania, wywołane są przez nieprawidłową składnię instrukcji. W efekcie otrzymujemy komunikat z numerem linii z błędem i jej treścią. Mała strzałka pokazuje token przed którym wykryto błąd.
>>> if True print("word")
File "<stdin>", line 1
if True print("word") # brak dwukropka PRZED print() (Py3.13)
^^^^^
SyntaxError: invalid syntax
Jeżeli instrukcja jest poprawna składniowo, to w dalszym ciągu mogą się pojawić błędy przy próbie jej uruchomienia (runtime errors). Takie błędy są nazywane wyjątkami i niekoniecznie muszą oznaczać katastrofę.
Wyjątek jest to zdarzenie, które może modyfikować przebieg sterowania w programów. W Pythonie wyjątki wywoływane są automatycznie w momencie wystąpienia błędów i mogą być wywoływane oraz przechwytywane przez nasz kod.
Wyjątki pozwalają nam wyskoczyć z dowolnie dużych części programu do kodu z programu obsługi wyjątku. Jest to spójny sposób reagowania na niezwykłe zdarzenia, narzędzie sterowania wysokiego poziomu.
Wyjątki są przetwarzane przez cztery instrukcje:
Najważniejsze powody wykorzystywania wyjątków:
Kiedy pojawi się błąd w czasie wykonywania programu, tworzony jest wyjątek. Zwykle wtedy program jest zatrzymywany, a Python wypisuje komunikat o błędzie. Tak działa domyślny program obsługi wyjątków. Standardowo komunikat o błędzie obejmuje zgłoszony wyjątek wraz ze śladem stosu, czyli listą wszystkich wierszy oraz funkcji aktywnych w momencie, kiedy nastąpił wyjątek.
# Przykłady poleceń powodujących wyjątki.
# Dzielenie przez zero - ZeroDivisionError.
print(23/0)
# Odwołanie się do nieistniejącego elementu listy - IndexError.
alist = [] # pusta lista
print(alist[5])
# Odwołanie się do nieistniejącego klucza w słowniku - KeyError.
adict = {} # pusty słownik
print(adict["key"])
# Otwarcie do czytania nieistniejącego pliku - IOError.
afile = open("nie_istnieje.txt", "r")
Czasem nie chcemy, aby program zatrzymał się po wystąpieniu wyjątku. Wtedy należy opakować wywołanie w instrukcję try/except/else w celu samodzielnego przechwycenia wyjątku. Jeżeli zależy nam na wykonaniu pewnych działań końcowych, niezależnych od wystąpienia wyjątku, to stosujemy instrukcję try/finally.
Od Pythona 2.5 mamy jedną instrukcję try/except/finally, czyli bloki except i finally mogą wystąpić w jednej instukcji try.
L = []
# L = [1, 2, 3]
try:
print(L[1])
except IndexError: # przechwycenie wyjątku
print("mam wyjątek") # nasz program obsługi wyjątku
else:
print("nie było wyjątku")
print("kontynuuję")
Jeżeli podczas wykonywania bloku try nie wystąpił
wyjątek, to następnie będzie wykonany blok else,
a potem instrukcje poza instrukcją try/except.
Jeżeli podczas wykonywania bloku try wystąpił
wyjątek IndexError, to następnie będzie wykonany blok
except IndexError,
a potem instrukcje poza instrukcją try/except.
Jeżeli podczas wykonywania bloku try wystąpił
wyjątek inny niż IndexError,
to wyjątek będzie przekazany wyżej
(szczegóły zależą od położenia instrukcji try/except).
L = []
# L = [1, 2, 3]
try:
print(L[1])
finally: # działania końcowe
print("zawsze wykonane")
print("kontynuuję")
Jeżeli podczas wykonywania bloku try nie wystąpił
wyjątek, to następnie będzie wykonany blok finally,
a potem instrukcje poza instrukcją try/finally.
Jeżeli podczas wykonywania bloku try wystąpił
wyjątek, to następnie będzie wykonany blok finally,
ale potem wyjątek będzie przekazany wyżej (szczegóły zależą od położenia
instrukcji try/finally).
Po otwarciu pliku do czytania również mogą się pojawić różne błędy, które przerwą program. Można zastosować konstrukcję try/finally, aby na pewno zamknąć otwarty plik. Obecnie zaleca się korzystanie z menedżera kontekstu (instrukcja with).
afile = open("tekstowy.txt") # najpierw otwieramy plik tekstowy
try: # pracujemy na pliku
text = afile.read() # czytamy cały tekst
finally: # na pewno zamkniemy plik
afile.close()
Sprawdzenie poprawności nazwy pliku podanej przez użytkownika można wykonać przy pomocy wyjątków.
# Bezpieczna obsługa pliku.
#filename = raw_input("Podaj nazwę pliku: ") # Py2
filename = input("Podaj nazwę pliku: ") # Py3
try: # pierwszy blok
afile = open(filename, "r")
except Exception: # drugi blok (w razie wyjątku)
print("Nie ma pliku o nazwie {}".format(filename))
else: # trzeci blok opcjonalny (nie było wyjątku)
print("Plik został otwarty")
Można zamknąć taką funkcjonalność wewnątrz funkcji.
# Funkcja zwraca True, jeżeli plik istnieje, lub False, w przeciwnym razie.
def if_exist(filename):
try:
afile = open(filename)
afile.close()
return True
except Exception:
return False
Ogólny format instrukcji try/except/else/finally zawiera wiele opcjonalnych bloków z programami obsługi, choć musi pojawić się przynajmniej jeden.
# Składnia.
try:
instrukcje # podstawowe działanie instrukcji
except ExceptionClass1: # przechwytuje wskazany wyjątek
instrukcje
except (ExceptionClass2, ExceptionClass3): # przechwytuje wymienione wyjątki
instrukcje
except ExceptionClass4 as exception1: # przechwytuje wyjątek i jego instancję
instrukcje
except (ExceptionClass5, ExceptionClass6) as exception2: # przechwytuje wyjątki i instancję
instrukcje
except: # przechwytuje wszystkie (pozostałe) wyjątki
instrukcje
else: # działania przy braku zgłoszenia wyjątku
instrukcje
finally: # działania końcowe
instrukcje
Należy ostrożnie korzystać z pustej części except, ponieważ może przechwycić nieoczekiwane wyjątki systemowe niezwiązane z naszym kodem albo wyjątki przeznaczone dla innych programów obsługi. Lepsza jest postać except Exception, która ignoruje wyjątki powiązane z systemowymi wyjściami z programu.
Część else jest pomocna przy ustaleniu, czy wyjątek nie został zgłoszony, czy wystąpił i został obsłużony.
PEP 765 zawiera uzasadnienie, dlaczego w części finally Python nie pozwala na użycie instrukcji return, break i continue. Dzięki temu zapobiega się wielu błędom trudnym do wykrycia, a także poprawia się bezpieczeństwo.
Do ręcznego wywoływania wyjątków służy instrukcja raise.
try:
raise IndexError # rzucamy wyjątek IndexError
except IndexError: # przechwycenie wyjątku
print("mam wyjątek") # nasz program obsługi wyjątku
print("kontynuuję")
>>> raise IndexError Traceback (most recent call last): File "<stdin>", line 1, in <module> IndexError
>>> raise IndexError, "message" # stara składnia Py2 Traceback (most recent call last): File "<stdin>", line 1, in <module> IndexError: message
>>> raise IndexError("message") # nowa składnia Py2.7, Py3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: message
Do wywołania wyjątku można także wykorzystać wyrażenie assert, które jest wykorzystywana głównie przy debugowaniu kodu. Jest to pomoc dla programistów do wyszukiwania błędów w programie, sprawdza się występowanie "niemożliwych" sytuacji. Nie jest to mechanizm do obsługi błędów czasu wykonania (run-time errors). AssertionError nie powinien się nigdy pojawić, jeżeli program nie ma błędów.
>>> assert False, "message" Traceback (most recent call last): File "<stdin>", line 1, in <module> AssertionError: message