6. Винятки
Тепер розглянемо винятки.
![]() |
Перший скрипт ілюструє необхідність обробки винятків.
# -*- coding=utf-8 -*-
# викликається помилка
x=4/0
Ми навмисно викликаємо помилку, щоб побачити інформацію, яку видає інтерпретатор. Результат на екрані такий:
У рядку 4 вказано:
- тип винятку: ZeroDivisionError;
- відповідне повідомлення про помилку: integer division or modulo by zero. Воно англійською мовою. Це те, що, можливо, варто змінити.
Основне правило програмування полягає в тому, що слід робити все можливе, щоб уникнути «неконтрольованих» збоїв, таких як наведений вище. Навіть у разі помилки програма повинна коректно завершуватися, надаючи інформацію про помилку, що сталася.
Синтаксис обробки винятків такий:
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
У блоці try виконання дій припиняється, щойно виникає виняток. У цьому випадку виконання продовжується з дій блоку except.
Блок
except [MyException, ...]:
перехоплює винятки типу MyException або похідні від нього. Коли в блоці try виникає виняток, інтерпретатор перевіряє блоки except, пов'язані з блоком try, у тому порядку, в якому вони були написані. Він зупиняється на першому блоці except, що дозволяє обробити виняток, який виник. Якщо він не знаходить жодного, виняток передається до методу, що його викликав. Якщо цей метод містить блок try/except, виняток знову обробляється; інакше він продовжує передаватися вгору по ланцюжку викликаних методів. В крайньому випадку він досягає інтерпретатора Python. Тоді інтерпретатор зупиняє виконання програми та виводить повідомлення про помилку, подібне до наведеного в попередньому прикладі. Отже, правило полягає в тому, що головна програма повинна перехоплювати всі винятки, які можуть передаватися з викликаних методів.
Виняток містить інформацію про помилку, що сталася. Її можна отримати за допомогою такого синтаксису:
except MyException as informations:
«informations» — це кортеж, що містить інформацію, пов’язану з винятком.
Синтаксис
except MyException, erreur:
присвоює змінній erreur повідомлення про помилку, пов'язане з винятком.
Щоб викликати виняток, використовується синтаксис
raise MyException(param1, param2, ...)
, де найчастіше MyException є класом, похідним від класу Exception. Параметри, передані конструктору класу, будуть доступні в блоці except структур перехоплення винятків.
Ці поняття проілюстровано в наступному скрипті.
Наступний скрипт явно обробляє помилки:
# -*- coding=utf-8 -*-
i=0
# викликаємо помилку та обробляємо її
x=2
try:
x=4/0
except ZeroDivisionError, erreur:
print ("%s : %s ") % (i, erreur)
# значення x не змінилося
print "x=%s" % (x)
# починаємо спочатку
i+=1
try:
x=4/0
except Exception, erreur:
# перехоплюємо найзагальніше виключення
print ("%s : %s ") % (i, erreur)
# можна перехоплювати кілька типів винятків
i+=1
try:
x=4/0
except ValueError, erreur:
# це виключення тут не виникає
print ("%s : %s ") % (i, erreur)
except Exception, erreur:
# перехоплюємо найзагальніше виключення
print ("%s : (Exception) %s ") % (i, erreur)
except ZeroDivisionError, erreur:
# перехоплюється конкретний тип
print ("%s : (ZeroDivisionError) %s ") % (i, erreur)
# повторюємо, змінюючи порядок
i+=1
try:
x=4/0
except ValueError, erreur:
# це виключення тут не виникає
print ("%s : %s ") % (i, erreur)
except ZeroDivisionError, erreur:
# перехоплюється конкретний тип
print ("%s : (ZeroDivisionError) %s ") % (i, erreur)
except Exception, erreur:
# перехоплюється найзагальніше виключення
print ("%s : (Exception) %s ") % (i, erreur)
# клаузула except без аргументів
i+=1
try:
x=4/0
except:
# не враховується ні характер винятку, ні повідомлення про помилку
print ("%s : il y a eu un probleme ") % (i)
# інший тип винятку
i+=1
try:
x=int("x")
except ValueError, erreur:
print ("%s : %s ") % (i, erreur)
# виняток передає інформацію у вигляді кортежу, доступного для програми
i+=1
try:
x=int("x")
except ValueError as infos:
print ("%s : %s ") % (i, infos)
# можна генерувати винятки
i+=1
try:
raise ValueError("param1","param2","param3")
except ValueError as infos:
print ("%s : %s ") % (i, infos)
# можна створювати власні винятки
class MyError(Exception):
pass
# викликається виняток MyError
i+=1
try:
raise MyError("info1","info2", "info3")
except MyError as infos:
print ("%s : %s ") % (i, infos)
# викликається виняток MyError
i+=1
try:
raise MyError("mon msg d'erreur")
except MyError, erreur:
print ("%s : %s ") % (i, erreur)
# можна генерувати об’єкт будь-якого типу
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)
# блока finally завжди виконується
# незалежно від того, чи виникло виключення
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)
Примітки:
- рядки 6–9: обробляється поділ на нуль;
- рядок 8: перехоплюється саме те виключення, яке виникло;
- рядок 11: через виникнення винятку змінна x не отримала значення і, отже, не змінила свого значення;
- рядки 15–19: повторюємо те саме, але перехоплюємо виняток вищого рівня типу Exception. Оскільки виняток ZeroDivisionError походить від класу Exception, оператор except зупинить його;
- рядки 23–33: додано кілька умов except для обробки різних типів винятків. Буде виконано лише одну умову except або жодну, якщо виняток не відповідає жодній умові except;
- рядки 51–55: оператор except може не мати аргументів. У цьому випадку він зупиняє всі винятки;
- рядки 59–62: вводять виняток ValueError;
- рядки 66–69: отримується інформація, що передається винятком;
- рядки 73–76: описують спосіб виклику винятку;
- рядки 79–84: ілюструють використання власного класу винятків MyError;
- рядки 79–80: клас MyError просто походить від базового класу Exception. Він нічого не додає до свого базового класу. Але тепер його можна явно вказувати в операторах except;
- рядки 97–107: ілюструють, що в Python можна фактично запускати об’єкти будь-якого типу, а не лише об’єкти, похідні від класу Exception;
- рядки 109–127: ілюструють використання оператора finally.
Результати на екрані такі:
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
Цей новий скрипт ілюструє передачу винятків у ланцюжку методів, що викликають:
# -*- coding=utf-8 -*-
# власне виняток
class MyError(Exception):
pass
# три методи
def f1(x):
# винятки не обробляються — вони автоматично передаються вгору
return f2(x)
def f2(y):
# винятки не обробляються — вони автоматично передаються вище
return f3(y)
def f3(z):
if (z % 2) == 0:
# якщо z парне, генерується виняток
raise MyError("exception dans f3")
else:
return 2*z
#---------- main
# винятки передаються вгору по ланцюжку викликаних методів
# доти, доки якийсь метод не перехопить їх. У цьому випадку це буде main
try:
print f1(4)
except MyError as infos:
print "type :%s, arguments : %s" % (type(infos),infos)
# метод може доповнити винятки, які він передає далі
def f4(x):
try:
return f5(x)
except MyError as infos:
# виняток доповнюється, а потім перекидається
raise MyError(infos,"exception dans f4")
def f5(y):
try:
return f6(y)
except MyError as infos:
# виняток доповнюється, а потім перекидається
raise MyError(infos, "exception dans f5")
def f6(z):
if (z % 2) == 0:
# викликаємо виняток
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)
Примітки:
- у рядках 27–30, під час виклику main --> f1 --> f2 --> f3 (рядок 28), виняток MyError, згенерований f3, буде передаватися аж до main. Потім вона буде оброблена за допомогою оператора except у рядку 29;
- рядки 55–58: під час виклику main → f4 → f5 → f6 (рядок 56) виняток MyError, згенерований f6, буде простежуватися аж до main. Потім вона буде оброблена за допомогою оператора except у рядку 29. Цього разу під час проходження ланцюжка методів, що викликають, виняток MyError доповнюється інформацією, доданою кожним методом у ланцюжку.
Результати виведення на екран такі:
