Przyszedł czas na bardziej zaawansowany temat z nauki Pythona 💥! Wchodzimy na wyższy poziom i tutaj pokażę Ci czym w języku Python jest przechwytywanie wyjątków 😮! Poznasz aż 3 słowa kluczowe związane z wyjątkami: "try", "except" oraz "finally" 🔥! Ponadto dowiesz się kiedy, w jaki sposób i w jakim celu się je stosuje ❤️! Wystarczy zajrzeć do środka artykułu i przeczytać ze skupieniem, bo ostrzegam, że będzie dużo treści do przyswojenia 😇!
WYJAŚNIAM CZYM JEST PRZECHWYTYWANIE WYJĄTKÓW W JĘZYKU PYTHON!
Zaczniemy od wyjaśnienia sobie samego pojęcia "wyjątek" 1️⃣ bo rozumiem, że może groźnie brzmieć 🙃. Potem wyjaśnię czym jest przechwytywanie tychże wyjątków 2️⃣, a na końcu przejdziemy do kodu źródłowego biorąc pod uwagę wszystkie przypadki 3️⃣, tak aby każdy czytający miał przepis na każdą sytuację 😉!
CZYM JEST WYJĄTEK W PROGRAMOWANIU?
Wyjątek (ang. exception) w programowaniu (bo nie jest to coś istniejącego tylko w Pythonie 🐍) jest nieoczekiwanym zdarzeniem mogącym pojawić się podczas działania programu w momencie wystąpienia jakiegoś problemu ℹ️. Tutaj mówimy o przypadkach skrajnych, które pojawiają się podczas wystąpienia nieoczekiwanych zdarzeń przez funkcje czy obiekty 🔥.
Wyobraźmy sobie, że mamy do zapłacenia rachunek za prąd 🔌. "Domyślnym" przebiegiem zdarzeń jest wykonanie przelewu pocztą lub przez internet 💸. Może jednak zdarzyć się "wyjątek", że nie będziemy mieli pieniędzy na opłacenie rachunku i jeżeli nie opracujemy jakiegoś rozwiązania, to mamy poważny problem 😟. Choć w realnym życiu da się przewidzieć czy w najbliższych dniach będziemy w stanie pokryć płatność, czy nie, to jednak nie jest przez nas pożądaną sytuacją brak forsy na opłacenie rachunku 😐.
Warto w tym miejscu zaznaczyć, że to nie to samo, co instrukcja warunkowa ⚠️! W instrukcji "if" sami określamy przypadki, które chcemy uwzględnić. Gdy mowa o wyjątkach, to chodzi o sytuacje niespodziewane i niepożądane 🔥. Wówczas program "nie wie" jak się ma zachować w sytuacji, której się "nie spodziewał" ❓. Dochodzi wtedy do tzw. "zgłoszenia wyjątku", czyli wysłania sygnału o wystąpieniu sytuacji uniemożliwiającej wykonanie określonych instrukcji 🚨.
JAKIE SĄ PRZYKŁADY WYJĄTKÓW W PROGRAMOWANIU?
Przykładem takiego wyjątku może być próba otwarcia pliku, którego nie ma ❌. Podstawowym scenariuszem jest występowanie pliku w podanej lokalizacji, o podanej nazwie i rozszerzeniu. Wtedy zakładamy, że on tam jest i grzecznie czeka na wykonanie operacji 👍. Sytuacja, w której tego pliku tam nie ma, jest niepożądana i funkcja, która miała otworzyć dany plik nie wie co ma w tej sytuacji zrobić. Wtedy dochodzi do zgłoszenia wyjątku 📣.
CO SIĘ DZIEJE, JAK ZGŁOSZONY WYJĄTEK NIE JEST PRZECHWYCONY?
Jeżeli zgłaszany wyjątek nie zostanie "obsłużony" czy "przechwycony", to domyślnie skutkuje to natychmiastowym przerwaniem działania programu i awaryjnego zakończenia procesu w systemie operacyjnym 💥! Gdy kiedykolwiek doświadczyłeś(-aś) sytuacji, że podczas grania czy korzystania z programu trafiłeś(-aś) na błąd o treści "Unhandled exception" i po kliknięciu przycisku "OK" program się zamykał, to właśnie dlatego, że dany wyjątek nie został "przechwycony", a program został brutalnie przerwany 💡!
Teraz jest idealny moment na wyjaśnienie "przechwytywania wyjątków" 🙂!
CZYM JEST PRZECHWYTYWANIE WYJĄTKÓW W PROGRAMOWANIU?
Gdy mowa o przechwytywaniu wyjątków w programowaniu, to chodzi o zdefiniowanie instrukcji co ma się wydarzyć w momencie zgłoszenia wyjątku przez program, gdy dana instrukcja się "wysypie" 👍. Zazwyczaj czyni się tak w celu osiągnięcia jednego z dwóch zamiarów 👇:
- przedstawienie użytkownikowi dokładnej przyczyny zgłoszenia wyjątku, aby miał szansę samodzielnej naprawy usterki, jeśli to możliwe (np. wypisanie komunikatu "mówiącego po ludzku", że nie znaleziono pliku o podanej nazwie),
- zrealizowanie pewnych instrukcji "naprawczych" celem kontynuowania dalszego działania programu, nie doprowadzając do jego zamknięcia (np. przywrócenie domyślnej konfiguracji programu po wykryciu wadliwej wartości w dostarczonych danych).
Jeżeli w skrypcie są zawarte instrukcje na okoliczność wystąpienia ściśle określonej sytuacji wyjątkowej, to mówimy, że wyjątek jest "przechwycony" bądź "obsłużony" ℹ️.
Zanim zacznę omawiać kod źródłowy, warto jeszcze wiedzieć jak odróżniać wyjątek od błędu 🔔!
CZYM W PROGRAMOWANIU RÓŻNI SIĘ WYJĄTEK OD BŁĘDU?
W programowaniu, wyjątek nie jest tym samym co błąd ❌! Oba pojęcia istotnie są związane z wystąpieniem sytuacji niepożądanych, aczkolwiek różnią się ważnym szczegółem.
Gdy zostaje zgłoszony wyjątek, to możemy coś jeszcze z nim zrobić i przechwytując go, mamy jakiś wpływ na dalszy ciąg 👍. W przypadku błędu, nic nie możemy zrobić. Dochodzi do bezdyskusyjnego zakończenia działania programu "bez pytania o zdanie" 💥.
W Pythonie zostało to pomieszane jedno z drugim i w nazwach wyjątków występuje określenie "Error". I to niedobrze, bo chociaż Python używa określenia "błąd", to jednak jest to wyjątek, a nie błąd ⛔! Zatem, kiedy w powłoce widzisz komunikaty z dopiskiem "Error", to w rzeczywistości są to zgłoszone wyjątki, które nie zostały obsłużone 💡. Pamiętaj o tym 🙂!
To wszystko z teorii ogólnej, a teraz przechodzimy do tematu już obracając się wokół Pythona 🐍! Na sam początek trzeba się dowiedzieć jaki kształt przyjmuje wyjątek w Pythonie 👀!
JAK WYGLĄDA WYJĄTEK W JĘZYKU PYTHON?
Gdy Python zgłasza wyjątek, w konsoli pojawia się treść różnych informacji na jego temat. Zazwyczaj przyjmuje to taką formę 👇:
Traceback (most recent call last):
File "[ścieżka do pliku]", line [numer wiersza], in
[treść wiersza, w której doszło do zgłoszenia wyjątku]
[oznaczenie graficzne dokładnego miejsca wystąpienia problemu]
[nazwa klasy wyjątku]: [krótki opis przyczyny zgłoszenia wyjątku] Komunikat o zgłoszonym wyjątku składa się z następujących wierszy 👇:
- zdanie z charakterystycznym hasłem "Traceback", które oznacza wypisanie stosu wywołań funkcji od uruchomienia programu do miejsca, w którym doszło do zgłoszenia wyjątku (jest to po to, aby programista mógł odtworzyć ciąg "przechodzenia" przez funkcje jedna po drugiej celem dotarcia do początku),
- wypisanie skryptu, numeru wiersza i modułu, w którym doszło do zgłoszenia wyjątku,
- wypisanie treści wiersza, w którym doszło do zgłoszenia wyjątku,
- ciąg znaków w postaci tyldy (~) i "dzióbka" skierowanego w górę (^) jako symboliczne oznaczenie dokładnego miejsca w wierszu, w którym doszło do zgłoszenia wyjątku,
- nazwa klasy wyjątku wraz z krótkim opisem powodu zgłoszenia wyjątku (w dalszym ciągu wyjaśniam dlaczego występuje klasa ℹ️).
W zależności od występowania skryptu w module i innych potencjalnych czynników, może wyglądać nieco inaczej niż ukazano wyżej, natomiast zazwyczaj będzie takiej postaci ℹ️.
W końcu można przejść do tłumaczenia samej budowy przechwytywania wyjątków w kodzie źródłowym 🎉!
JAK WYKONAĆ PRZECHWYTYWANIE WYJĄTKU W JĘZYKU PYTHON?
Aby w Pythonie zrealizować przechwytywanie wyjątku, należy zdefiniować 2 odrębne bloki instrukcji z podziałem na to, co może zawieść i co ma się stać w przypadku niepowodzenia 🎯. Python wspiera również dodatkowy blok do określania instrukcji, które mają się wykonać na samym końcu, bez względu na to, czy wyjątek został zgłoszony, czy też nie (on jest opcjonalny) ☑️.
Poniższa tabelka przedstawia wszystkie 3 słowa kluczowe i jakie jest znaczenie każdego z nich 👇:
| SŁOWO KLUCZOWE | ZNACZENIE |
| try | blok dla instrukcji "ryzykownych" (mogących się nie powieść) |
| except | blok dla instrukcji wykonywanych w przypadku zgłoszenia określonego wyjątku |
| finally | blok dla instrukcji wykonywanych niezależnie od rezultatu działania instrukcji "ryzykownych" |
Abyś w pełni zrozumiał(a) na czym to polega, musimy sobie dokładnie omówić każdą z 3 części konstrukcji 🔔!
CZYM JEST "TRY" W JĘZYKU PYTHON?
Blok "try" (ang. próbować) to miejsce dla instrukcji, które mogą zgłosić wyjątek ℹ️. Weźmy dla prostego przykładu dzielenie przez zero 👇:
print(16 / 0)Od razu wiadomo, że to spowoduje problem i sprawi, że program zgłosi błąd "ZeroDivisionError" (choć jak pisałem wyżej, określenie "błąd" jest w tym przypadku niewłaściwe) ❌. Aby uniknąć awaryjnego przerwania, należy tę instrukcję wstawić do bloku "try" 🔥. Tak wygląda przykład 👇:
try:
print(16 / 0)Szybka analiza samego zapisu 🔍! Blok "try" składa się z następujących elementów 👇:
- słowo kluczowe "try",
- znak dwukropka (obowiązkowa rzecz, bez której będzie błąd składniowy 🚫),
- instrukcje po zrobieniu obowiązkowego wcięcia (poprzez spację lub tabulator).
Efekt będzie taki, że po uruchomieniu programu ta sama instrukcja wykona się bezwarunkowo (to nie ulega zmianie), natomiast tutaj "podlega bacznej obserwacji" 👀 i w przypadku zgłoszenia wyjątku, dalszy ciąg będzie zależeć od kolejnego bloku, "except" 😉!
![]() |
Blok "try" w języku Python stanowi jedną z trzech części konstrukcji do przechwytywania wyjątków, w której umieszczamy instrukcje mogące zgłosić wyjątek.
CZYM JEST "EXCEPT" W JĘZYKU PYTHON?
Blok "except" (ang. poza/z wyłączeniem/z wyjątkiem), to jest miejsce do określania instrukcji, które mają się wykonać w momencie zgłoszenia wyjątku (czyli nie wykonują się za każdym razem!) ℹ️. On musi się znaleźć linijkę niżej po bloku "try" ⚠️! Tutaj Python pozwala określić jaki konkretnie wyjątek nas interesuje, natomiast o tym za momencik 😉.
JAK WYGLĄDA NAJPROSTSZY BLOK "EXCEPT" W JĘZYKU PYTHON?
To jest przykład najprostszej postaci, na razie bez określania jaki rodzaj wyjątku chcemy przechwycić 👇:
except:
print('Wykryto dzielenie przez zero!')Konstrukcja bloku "except" składa się z takich oto części 👇:
- słowo kluczowe "except",
- znak dwukropka (obowiązkowa rzecz, bez której będzie błąd składniowy 🚫),
- instrukcje po zrobieniu obowiązkowego wcięcia (poprzez spację lub tabulator).
To jest minimalna postać zawierające same elementarne rzeczy, a zaraz pokażę Ci jaka jeszcze występuje 😲.
Gdy uruchomimy program po wstawieniu obu bloków "try" i "except", to dojdzie do następujących konsekwencji 👇:
- program próbuje podzielić liczbę przez zero,
- po nieudanym wykonaniu ilorazu, program zgłasza wyjątek "ZeroDivisionError",
- wyjątek zostaje "przechwycony" przez blok "except",
- zostają wykonane wszystkie instrukcje zawarte w bloku "except",
- program wykonuje się dalej, po bloku "except".
W ten sposób, przechwytujemy od programu sygnał, że coś poszło nie tak i udaje się zapobiec awarii programu ❤️!
![]() |
Blok "except" w języku Python to druga z trzech części konstrukcji do przechwytywania wyjątków przeznaczona do określania instrukcji wykonywanych w momencie zgłoszenia wyjątku.
A teraz rozpatrzmy przykład, w którym chcemy wypisać w konsoli więcej informacji o samym wyjątku 📝!
W Pythonie, możemy określić jaki typ wyjątku chcemy przechwytywać poprzez wprowadzenie nazwy klasy "Exception" 😮! Jest to klasa bazowa wszystkich klas dotyczących "rodzajów" wyjątków ℹ️. Na przykład "ZeroDivisionError" jest jednym z tych "rodzajów" 😊.
JAK STWORZYĆ BLOK "EXCEPT" W JĘZYKU PYTHON ZE ZDEFINIOWANYM TYPEM WYJĄTKU?
Wpisując konkretny typ klasy zaraz po słowie kluczowym "except", określamy typ wyjątku jaki mamy zamiar przechwytywać 🧨. To jest najprostszy przykład 👇:
except Exception:
print('Wykryto dzielenie przez zero!')Taki zapis niczego nie zmienia w działaniu 🙂 - dalej będzie wypisanie komunikatu po zgłoszeniu wyjątku o dzieleniu przez zero, a tak naprawdę dowolnego wyjątku, bo napisaliśmy najbardziej ogólny typ, "Exception" ℹ️.
Dlatego pokazuję od razu drugi przykład, w którym poza typem wyjątku piszemy jeszcze jeden dodatkowy element 👇:
except Exception as exception:
print(exception)W tym przypadku, oprócz typu wyjątku jaki chcemy przechwycić, dopisujemy jeszcze alias pozwalający na późniejsze powoływanie się do tego typu wyjątku 🚀. To jest konkretnie ten fragment 👇:
as exception:Gdy uruchomimy sobie taki program, w momencie zgłoszenia wyjątku pojawi się jednozdaniowy opis przyczyny zgłoszenia wyjątku ✅. W ten sposób możesz pisać dowolne instrukcje w oparciu powyższy alias, więc warto znać ten zapis 😊.
![]() |
Blok "except" w języku Python może dotyczyć jakiegoś określonego typu wyjątku i posiadać alias, który pozwala na odwoływanie się do wskazanego typu wyjątku wewnątrz instrukcji.
To wyczerpuje temat w obrębie przechwytywania jednego wyjątku. A co, gdy potrzebujemy przechwytywać dwa lub więcej różnych typów wyjątków naraz 🤔?
JAK W JĘZYKU PYTHON PRZECHWYTYWAĆ WIELE WYJĄTKÓW NARAZ?
Przechwytywanie wielu wyjątków w języku Python w jednym bloku, jest możliwe poprzez zdefiniowanie tego samego bloku "except" tyle razy, ile potrzebujesz 👌. Załóżmy, że wśród naszych instrukcji "ryzykownych" (tych, które znajdują się w bloku "try"), znajduje się taka, która może spowodować zgłoszenie wyjątku innego, niż dzielenie przez zero 😮!
Na przykład za drugą taką instrukcję podstawmy próbę dodania łańcucha znaków do liczby całkowitej (bez ułamka) 👇:
print(6 + '2')To jest oczywiście jedna z operacji niedozwolonych 😉. Czyli teraz załóżmy, że mamy 2 instrukcje ryzykowne i każda z nich może zgłosić inny rodzaj wyjątku. W przypadku powyższej instrukcji będzie to wyjątek typu "TypeError", czyli nieobsługiwane typy operandów (argumentów operatora) dla operacji dodawania pomiędzy łańcuchem, a liczbą 💡. Ponownie uczulam na mylące nazewnictwo ⚠️! To nie jest błąd mimo tego, iż występuje w nazwie "Error" ⛔. To jest typ klasy przeznaczonej dla wyjątków 🔥!
Abyśmy obsłużyli OBA te wyjątki, musimy zdefiniować dwa osobne bloki "except", pisząc na przykład tak 👇:
except ZeroDivisionError as zeroDivisionError:
print(zeroDivisionError)
except TypeError as typeError:
print(typeError)To oznacza następujące fakty 👇:
- blok przeznaczony do obsługi wyjątku próby dzielenia przez zero wykona się wtedy i tylko wtedy, gdy zostanie zgłoszony wyjątek typu "ZeroDivisionError",
- blok przeznaczony do obsługi wyjątku próby dodania łańcucha do liczby wykona się wtedy i tylko wtedy, gdy zostanie zgłoszony wyjątek typu "TypeError".
Czyli zauważ, że to zachowanie do złudzenia przypomina działanie bloków "else if" instrukcji warunkowej 🎉! Sprawdzamy jedno, potem drugie, potem n-te i tak dalej 🙂.
![]() |
Przechwytywanie wyjątków w języku Python może składać się z wielu bloków "except" i każdy z nich być przeznaczony do obsługi innego typu wyjątku.
Tylko zwracam uwagę, iż kolejność tychże bloków odgrywa kolosalną rolę 😳!
CZY KOLEJNOŚĆ DEFINIOWANIA BLOKÓW "EXCEPT" W JĘZYKU PYTHON MA ZNACZENIE?
Należy mieć równie ważną świadomość, że w Pythonie, tak jak w instrukcji warunkowej kolejność sprawdzania warunków ma znaczenie, tak i tutaj powinniśmy definiować bloki "except" zaczynając od najbardziej specyficznych przypadków, "schodząc w dół" do najbardziej ogólnego 💥!
Jeżeli dla przykładu, chcemy przechwycić 2 różne wyjątki (jakiś konkretny i każdy inny dowolny) i napiszemy 2 bloki "except" w taki sposób 👇:
except Exception as exception:
print(exception)
except TypeError as typeError:
print(typeError)To w przypadku obojętnie jakiego wyjątku, zostanie zrealizowany blok "except" dla bazowego typu "Exception" i program nigdy nie dojdzie do tego bloku poniżej 😱! W momencie zgłoszenia wyjątku przez instrukcję zawartą w bloku "try", Python sprawdza wszystkie bloki "except" w poszukiwaniu pasującego typu zaczynając zawsze od pierwszego od góry ⚠️!
"Exception" jest typem wszystkich wyjątków, więc uwzględni ten typ i nie będzie procedował dalej (tak jak w instrukcji warunkowej dla bloków "else if" 💡) 😐. Dlatego jeżeli kiedykolwiek będziesz potrzebować sprawdzania wielu wyjątków i pomiędzy zdefiniowanymi typami zachodzą jakieś relacje od strony dziedziczenia po klasie bazowej, zawsze zaczynaj od najbardziej specyficznych typów wyjątków 🫵!
To wszystko co musisz wiedzieć na temat bloków "except", to przechodzimy do ostatniej części: "finally"! Aż powiedziałoby się: "w końcu" 😁!
CZYM JEST "FINALLY" W JĘZYKU PYTHON?
Blok "finally" (ang. wreszcie/w końcu/ostatecznie) jest ostatnim z trzech słów kluczowych odnoszących się do przechwytywania wyjątków w języku Python, w którym to definiujemy instrukcje jakie mają się wykonać niezależnie od rezultatu działania instrukcji "ryzykownych" (zawartych w bloku "try") ℹ️. Jest to jedyna część ze wszystkich trzech, która nie musi być zdefiniowana, aby doszło do uruchomienia programu ✔️. Umieszczamy ją zawsze po ostatnim bloku "except" i nigdzie indziej (bo błąd składniowy 🚫!).
Nie ma znaczenia czy dojdzie do zgłoszenia wyjątku, czy nie - blok "finally" wykona się zawsze, na samym końcu 😉! Oto przykład najprostszego użycia 👇:
finally:
print('Koniec obsługi wyjątków')To przyniesie taki efekt, że komunikat pojawi się niezależnie od jednego z 2 możliwych scenariuszy 👇:
- wszystkie instrukcje "ryzykowne" (w bloku "try") wykonają się pomyślnie i nie dojdzie do zgłoszenia któregokolwiek wyjątku,
- któraś instrukcja "ryzykowna" się nie powiedzie i dojdzie do zgłoszenia któregokolwiek wyjątku.
Bez względu na to, czy wszystko wykona się poprawnie, czy nie, to wykona się zawsze, zaraz po blokach "try" i "except" 👍!
![]() |
Blok "finally" jest ostatnią z trzech części konstrukcji do przechwytywania wyjątków i wewnątrz niej umieszczamy instrukcje, które mają się wykonać niezależnie od przebiegu zdarzeń (czy dojdzie do zgłoszenia wyjątku, czy nie).
W tym miejscu możesz sobie pomyśleć: "na co mi to, skoro można równie dobrze zdefiniować w dalszym ciągu instrukcje na zewnątrz, zaraz po tym przechwytywaniu i wyjdzie na jedno?". Nie do końca "wyjdzie na jedno" 😏!
CZY DODANIE BLOKU "FINALLY" W JĘZYKU PYTHON ROBI JAKĄKOLWIEK RÓŻNICĘ?
W Pythonie, jeżeli przechwytywanie wyjątków znajduje się poza jakąkolwiek funkcją, to blok "finally" nie robi żadnej różnicy 🙂. Mając jakiekolwiek instrukcje do wykonania w dalszym ciągu, zarówno to 👇:
try:
print(16 / 0)
except Exception as exception:
print(exception)
finally:
print('Koniec obsługi wyjątków')jak i to 👇:
try:
print(16 / 0)
except Exception as exception:
print(exception)
print('Koniec obsługi wyjątków')przyniesie identyczny efekt ✅. Co innego, jak cała ta konstrukcja jest umieszczona w treści funkcji, a ona ma zwrócić jakąś wartość na wyjściu 🔥. Wtedy gdy zapiszemy tak 👇:
def get_division():
result = 0
try:
result = 16 / 0
except:
return -1
print('Koniec obsługi wyjątków')
return resultto komunikat nie zostanie wyświetlony z powodu słowa "return" umieszczonego w bloku "except" 😳. Za to w poniższej sytuacji 👇:
def get_division():
result = 0
try:
result = 16 / 0
except:
return -1
finally:
print('Koniec obsługi wyjątków')
return resultjuż się wyświetli, mimo wcześniejszego zwrócenia wartości na wyjściu 😮!!! Także możemy powiedzieć, że blok "finally" zapewnia wykonanie instrukcji pomiędzy "przejściami" przez funkcje (choć jest to uproszczone stwierdzenie) 🚀!
Generalnie zasada jest taka, aby stosować blok "finally" wobec instrukcji, które dotyczą zakończenia operacji na czymś np. zamknięcie strumienia do obsługi pliku ℹ️. Wtedy należy je "związać" blokiem "finally", aby mieć pewność, że te instrukcje wykonają się we właściwym momencie, a nie kilka chwil za późno ☑️.
Wszystko z elementarnej wiedzy na temat przechwytywania wyjątków ✅. To jest tak rozległy temat, że można spokojnie napisać o nim jeszcze kilka solidnych sztuk artykułów, natomiast tutaj zależało mi na rzetelnym wprowadzeniu, bo to zagadnienie może sprawiać na początku niemałe problemy 🙂.




