6. Wyjątki
Zajmiemy się teraz wyjątkami.
![]() |
Pierwszy skrypt ilustruje konieczność obsługi wyjątków.
# -*- coding=utf-8 -*-
# wywołuje się błąd
x=4/0
Celowo wywołujemy błąd, aby zobaczyć informacje generowane przez interpreter. Wynik wyświetlany na ekranie jest następujący:
Wiersz 4 podaje:
- typ wyjątku: ZeroDivisionError;
- powiązany komunikat o błędzie: integer division or modulo by zero. Jest on w języku angielskim. Można to zmienić.
Podstawową zasadą programowania jest to, że należy zrobić wszystko, aby uniknąć „niekontrolowanych” awarii, takich jak ta powyżej. Nawet w przypadku błędu program musi zakończyć działanie w sposób poprawny, podając informacje o błędzie, który wystąpił.
Składnia obsługi wyjątków jest następująca:
try:
actions susceptibles de lancer une exception
except [classe d'exception, ...]:
actions de gestion de l'exception
finally:
actions toujours exécutées qu'il y ait exception ou non
W bloku try wykonywanie akcji zostaje przerwane, gdy tylko wystąpi wyjątek. W takim przypadku wykonywanie jest kontynuowane zgodnie z akcjami zawartymi w klauzuli except.
Klauzula
except [MyException, ...]:
przechwytuje wyjątki typu MyException lub pochodne. Gdy w bloku try wystąpi wyjątek, interpreter sprawdza klauzule **except powiązane z tym blokiem w kolejności, w jakiej zostały zapisane. Zatrzymuje się na pierwszej klauzuli except, która pozwala obsłużyć wystąpiony wyjątek. Jeśli nie znajdzie żadnej, wyjątek jest przekazywany do metody wywołującej. Jeśli ta zawiera blok try/except**, wyjątek jest ponownie obsługiwany; w przeciwnym razie jest on przekazywany dalej w górę łańcucha wywoływanych metod. W ostateczności dociera do interpretera języka Python. Interpreter zatrzymuje wówczas wykonywany program i wyświetla komunikat o błędzie podobny do tego pokazanego w poprzednim przykładzie. Zasada jest zatem taka, że program główny musi przechwycić wszystkie wyjątki, które mogą zostać przekazane z wywołanych metod.
Wyjątek zawiera informacje o błędzie, który wystąpił. Można je uzyskać za pomocą następującej składni:
except MyException as informations:
informacje to krotka zawierająca dane związane z wyjątkiem.
Składnia
except MyException, erreur:
przypisuje do zmiennej erreur komunikat o błędzie związany z wyjątkiem.
Aby wygenerować wyjątek, stosuje się składnię
raise MyException(param1, param2, ...)
, gdzie najczęściej MyException jest klasą pochodną klasy Exception. Parametry przekazane do konstruktora klasy będą dostępne w klauzuli „except” struktur przechwytywania wyjątków.
Poniższy skrypt ilustruje te koncepcje.
Poniższy skrypt w sposób jawny obsługuje błędy:
# -*- coding=utf-8 -*-
i=0
# wywołuje się błąd i obsługuje się go
x=2
try:
x=4/0
except ZeroDivisionError, erreur:
print ("%s : %s ") % (i, erreur)
# wartość x nie uległa zmianie
print "x=%s" % (x)
# próbujemy ponownie
i+=1
try:
x=4/0
except Exception, erreur:
# przechwytujemy najbardziej ogólny wyjątek
print ("%s : %s ") % (i, erreur)
# można przechwycić kilka rodzajów wyjątków
i+=1
try:
x=4/0
except ValueError, erreur:
# ten wyjątek nie występuje w tym miejscu
print ("%s : %s ") % (i, erreur)
except Exception, erreur:
# przechwytujemy najbardziej ogólny wyjątek
print ("%s : (Exception) %s ") % (i, erreur)
except ZeroDivisionError, erreur:
# przechwytuje się konkretny typ
print ("%s : (ZeroDivisionError) %s ") % (i, erreur)
# próbujemy ponownie, zmieniając kolejność
i+=1
try:
x=4/0
except ValueError, erreur:
# ten wyjątek nie występuje w tym miejscu
print ("%s : %s ") % (i, erreur)
except ZeroDivisionError, erreur:
# przechwytujemy konkretny typ
print ("%s : (ZeroDivisionError) %s ") % (i, erreur)
except Exception, erreur:
# przechwytujemy najbardziej ogólny wyjątek
print ("%s : (Exception) %s ") % (i, erreur)
# klauzula except bez argumentów
i+=1
try:
x=4/0
except:
# nie interesuje nas ani rodzaj wyjątku, ani komunikat o błędzie
print ("%s : il y a eu un probleme ") % (i)
# inny typ wyjątku
i+=1
try:
x=int("x")
except ValueError, erreur:
print ("%s : %s ") % (i, erreur)
# wyjątek przenosi informacje w postaci krotki dostępnej dla programu
i+=1
try:
x=int("x")
except ValueError as infos:
print ("%s : %s ") % (i, infos)
# można wywoływać wyjątki
i+=1
try:
raise ValueError("param1","param2","param3")
except ValueError as infos:
print ("%s : %s ") % (i, infos)
# można tworzyć własne wyjątki
class MyError(Exception):
pass
# wywołuje się wyjątek MyError
i+=1
try:
raise MyError("info1","info2", "info3")
except MyError as infos:
print ("%s : %s ") % (i, infos)
# wywołuje się wyjątek MyError
i+=1
try:
raise MyError("mon msg d'erreur")
except MyError, erreur:
print ("%s : %s ") % (i, erreur)
# można wywołać dowolny typ obiektu
class Objet:
def __init__(self,msg):
self.msg=msg
def __str__(self):
return self.msg
i+=1
try:
raise Objet("pb...")
except Objet as erreur:
print "%s : %s" % (i, erreur)
# klauzula finally jest zawsze wykonywana
# niezależnie od tego, czy wystąpi wyjątek, czy nie
i+=1
x=None
try:
x=1
except:
print "%s : exception" % (i)
finally:
print "%s : finally x=%s" % (i,x)
i+=1
x=None
try:
x=2/0
except:
print "%s : exception" % (i)
finally:
print "%s : finally x=%s" % (i,x)
Uwagi:
- wiersze 6–9: obsługa dzielenia przez zero;
- wiersz 8: przechwytuje się dokładnie ten wyjątek, który wystąpił;
- wiersz 11: z powodu wystąpienia wyjątku zmienna x nie otrzymała wartości i w związku z tym jej wartość nie uległa zmianie;
- wiersze 15–19: powtarzamy tę samą procedurę, ale przechwytując wyjątek wyższego poziomu typu Exception. Ponieważ wyjątek ZeroDivisionError pochodzi od klasy Exception, klauzula except go zatrzyma;
- wiersze 23–33: umieszczamy kilka klauzul except, aby obsłużyć różne typy wyjątków. Wykonana zostanie tylko jedna klauzula except lub żadna, jeśli wyjątek nie spełnia warunków żadnej klauzuli except;
- wiersze 51–55: klauzula except może nie mieć żadnego argumentu. W takim przypadku zatrzymuje wszystkie wyjątki;
- wiersze 59–62: wprowadzają wyjątek ValueError;
- wiersze 66–69: pobierane są informacje przekazywane przez wyjątek;
- wiersze 73–76: przedstawiają sposób wywołania wyjątku;
- wiersze 79–84: ilustrują wykorzystanie własnej klasy wyjątku MyError;
- wiersze 79–80: klasa MyError jest po prostu pochodną klasy bazowej Exception. Nie dodaje ona niczego do swojej klasy bazowej. Teraz jednak można ją wyraźnie wymienić w klauzulach except;
- wiersze 97–107: pokazują, że w języku Python można w rzeczywistości rzucać dowolnym typem obiektu, a nie tylko obiektami pochodnymi od klasy Exception;
- wiersze 109–127: ilustrują użycie klauzuli finally.
Wyniki wyświetlane na ekranie są następujące:
0 : integer division or modulo by zero
x=2
1 : integer division or modulo by zero
2 : (Exception) integer division or modulo by zero
3 : (ZeroDivisionError) integer division or modulo by zero
4 : il y a eu un probleme
5 : invalid literal for int() with base 10: 'x'
6 : invalid literal for int() with base 10: 'x'
7 : ('param1', 'param2', 'param3')
8 : ('info1', 'info2', 'info3')
9 : mon msg d'erreur
10 : pb...
11 : finally x=1
12 : exception
12 : finally x=None
Ten nowy skrypt ilustruje przekazywanie wyjątków w łańcuchu wywołujących metod:
# -*- coding=utf-8 -*-
# wyjątek własny
class MyError(Exception):
pass
# trzy metody
def f1(x):
# nie obsługujemy wyjątków – są one automatycznie przekazywane w górę
return f2(x)
def f2(y):
# nie obsługujemy wyjątków – są one automatycznie przekazywane w górę
return f3(y)
def f3(z):
if (z % 2) == 0:
# jeśli z jest parzyste, zgłaszany jest wyjątek
raise MyError("exception dans f3")
else:
return 2*z
#---------- main
# wyjątki są przekazywane w górę łańcucha wywoływanych metod
# aż do momentu, gdy jakaś metoda je przechwyci. W tym przypadku będzie to metoda main
try:
print f1(4)
except MyError as infos:
print "type :%s, arguments : %s" % (type(infos),infos)
# metoda może wzbogacić wyjątki, które przekazuje dalej
def f4(x):
try:
return f5(x)
except MyError as infos:
# wzbogacamy wyjątek, a następnie ponownie go rzucamy
raise MyError(infos,"exception dans f4")
def f5(y):
try:
return f6(y)
except MyError as infos:
# wzbogacamy wyjątek, a następnie ponownie go rzucamy
raise MyError(infos, "exception dans f5")
def f6(z):
if (z % 2) == 0:
# wywołuje się wyjątek
raise MyError("exception dans f6")
else:
return 2*z
#---------- main
try:
print f4(4)
except MyError as infos:
print "type :%s, arguments : %s" % (type(infos),infos)
Uwagi:
- w wierszach 27–30, w wywołaniu main --> f1 --> f2 --> f3 (wiersz 28), wyjątek MyError wygenerowany przez f3 zostanie przekazany aż do main. Zostanie ona wówczas obsłużona przez klauzulę except w wierszu 29;
- wiersze 55–58: w wywołaniu main --> f4 --> f5 --> f6 (wiersz 56) wyjątek MyError wygenerowany przez f6 zostanie przekazany wstecz aż do main. Zostanie ona następnie przetworzona przez klauzulę except w wierszu 29. Tym razem, podczas przechodzenia w górę łańcucha wywołujących metod, wyjątek MyError jest wzbogacany o informacje dodawane przez każdą metodę, przez którą przechodzi.
Wyniki wyświetlane na ekranie są następujące:
