https://docs.python.org/3/library/exceptions.html
https://docs.python.org/3/reference/executionmodel.html#exceptions
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 ^ 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 (exception). 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 else: print("nie było wyjątku") print("kontynuuję")
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 będzie wykonany blok finally, a następnie instrukcje pod instrukcją try. Jeżeli podczas wykonywania bloku try wystąpił wyjątek, to będzie wykonany blok finally, ale potem wyjątek będzie przekazany wyżej.
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.
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.
Do ręcznego wywoływania wyjątków służy instrukcja raise.
try: raise IndexError except IndexError: # przechwycenie wyjątku print("mam wyjątek") # nasz program obsługi 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