Раніше про помилки згадувалося лише побіжно, але якщо ви пробували приклади, подані раніше, то напевно вже бачили деякі з них. Існують (принаймні) два окремі типи помилок: синтаксичні помилки та винятки.

Синтаксичні помилки

Синтаксичні помилки є можливо головною проблемою коли ви ще тільки вивчаєте мову Пітон:

>>> while True print 'Привіт, світе'
 File "<stdin>", line 1, in ?
 while True print 'Hello world'
 ^
SyntaxError: invalid syntax

Парсер повторює рядок, де було порушено правило, і показує маленьку "стрілку", що вказує на місце, де помилку було вперше помічено. Ця помилка була спричинена знаком (або принаймні помічена там), що передує стрілці : у цьому прикладі помилку знайдено перед ключовим словом print, тому що перед ним було пропущено двокрапку (":"). Також виводиться назва файла та номер рядка, щоб ви знали, де саме відбулася помилка, якщо ввід було отримано зі скрипта.

Винятки

Навіть коли твердження чи вираз синтаксично правильне, його виконання може призвести до помилки. Помилки, що відбуваються під час виконання програми звуться винятками і вони не є безумовно фатальними: незабаром ви довідаєтеся, як їх можна фільтрувати у програмах, написаних на Пітоні. Якщо помилка не обробляється програмою, то тоді її результатом буде повідомлення на зразок тих, що показані нижче:

>>> 10 * (1/0)
Traceback (most recent call last):
 File "<stdin>", line 1, in ?
ZeroDivisionError: integer division or modulo by zero
>>> 4 + spam*3
Traceback (most recent call last):
 File "<stdin>", line 1, in ?
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
 File "<stdin>", line 1, in ?
TypeError: cannot concatenate 'str' and 'int' objects

Останній рядок повідомлення про помилку вказує на те, що ж саме трапилося. Винятки належать до різних типів і назва типу виводиться як частина повідомлення. У наведених вище прикладах типами помилок є ZeroDivisionError (поділ на нуль), NameError (помилка назви) та TypeError (помилка типу). Назва помилки, що виводиться і є назвою вбудованого винятку, що відбувся. Це справедливо для всіх вбудованих винятків, але не завжди - для винятків, заданих користувачем (хоча це і корисна конвенція). Назви стандартних винятків - це вбудовані ідентифікатори (а не зарезервовані ключові слова).

Решта в цьому рядку - деталі, інтерпретація та значення яких залежить від типу помилки.

У попередній частині повідомлення йдеться про контекст, де відбулася помилка, у формі сліду стека. Загалом там подано лише рядки джерела; рядки, зчитані зі стандартного вводу не подаються.

Перелік вбудованих винятків та їхніх значень подається в Бібліотеці.

Фільтрування помилок

Можливо написати програми, здатні обробляти задані помилки. Розгляньмо наступний приклад, де програма вимагає вводу допоки не буде введено ціле число, але при цьому дозволяє користувачеві перервати програму (використовуючи Control-C чи щось інше, що розуміє операційна система); зауважте, що переривання, викликане користувачем, супроводжується створенням винятку KeyboardInterrupt.

>>> while True:
... try:
...     x = int(raw_input("Введіть, будь-ласка, ціле число: "))
...     break
... except ValueError:
...     print "Недійсне число. Спробуйте знову..."
...

Твердження try працює таким чином:

  • Спочатку виконується блок, розташований між ключовими словами try та except.
  • Якщо жодного винятку не відбулося, то твердження, розташовані за except пропускаються і таким чином закінчується виконання блоку try.
  • Якщо виняток трапився під час виконання одного з тверджень блоку try, то решта його тверджень пропускається.

Після цього, якщо тип помилки збігається з типом винятку, зазначеним після ключового слова except, то

виконуються твердження блоку except, після чого продовжується виконання коду, розташованого за твердженням

try.

  • Якщо виняток, що відбувся, не відповідає винятку, названому після ключового слова except, то він передається

зовнішнім твердженням try. Якщо виняток так і не було відфільтровано, то він вважається _неопрацьованим _

винятком і виконання програми зупиняється із виведенням помилки, як показано вище.

Твердження try може мати більш ніж одну конструкцію except для обробки різних помилок, але не більше ніж одну з них буде виконано. Оброблено буде лише помилки, що відбулися у відповідному блоці try, а не всередпні самих фільтрів. Конструкція except може опрацьовувати кілька винятків одночасно; при цьому їх назви оточуються дужками:

... except (RuntimeError, TypeError, NameError):
...     pass

Остання конструкція except може не вказувати назви винятків, що може використовуватися для обробки будь-якої помилки. Цю рису слід використовувати з надзвичайною обережністю, бо з її допомогою можна легко приховати справжні помилки у програмі! Вона також може служити для виводу повідомлення про помилку з повторним підняттям помилки (що може використовуватися для її подальшої обробки в середовищі, яке викликало даний код):

import sys

try:
 f = open('myfile.txt')
 s = f.readline()
 i = int(s.strip())
except IOError, (errno, strerror):
 print "Помилка вводу/виводу (%s): %s" % (errno, strerror)
except ValueError:
 print "Неможливо конвертувати дані в ціле число."
except:
 print "Несподівана помилка:", sys.exc_info()[0]
 raise

Твердження try ... except має необов'язкову конструкцію else ("інакше"), яка, якщо присутня, повинна закривати всі конструкції except. Це корисно для створення коду, що має виконатися в разі, якщо не відбулося жодного винятку. Наприклад::

for arg in sys.argv[1:]:
 try:
  f = open(arg, 'r')
 except IOError:
  print 'неможливо відкрити', arg
 else:
  print arg, 'має', len(f.readlines()), 'рядків'
  f.close()

Вживання блоку else - краще, ніж створення додаткового коду всередині блоку try, тому що дозволяє уникати випадкової обробки винятку, який не повинен був підніматися всередині коду, захищеного твердженням try ... except.

Виняток може мати певну величину, що асоціюється з ним. Ця величина також відома як аргумент винякту. Присутність і тип цього аргументу залежать від типу винятку.

Блок except може визначати змінну (або список) після назви винятку. Ця змінна прив'язується до реалізації винятку, чиї аргументи зберігаються у instance.args. Для зручності, реалізація винятку визначає __getitem__ та __str__, а отже вивід та доступ до аргументів може здійснюватися напряму без посилання на .args.

>>> try:
...  raise Exception('spam', 'eggs')
... except Exception, inst:
...  print type(inst) # реалізація винятку
...  print inst.args # аргументи збережено в .args
...  print inst # __str__ дозволяє виводити аргументи напряму
...  x, y = inst # __getitem__ дозволяє розпаковувати аргументи напряму
...  print 'x =', x
...  print 'y =', y
...
<type 'instance'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs

Якщо виняток має аргумент, то він виводиться як остання частина ("деталь") повідомлення для необроблених помилок.

Фільтри винятків обробляють не лише ті помилки, що відбулися безпосередньо у блоці try, але і ті, що виникають всередині функцій, які викликаються (навіть опосередковано) із блоку try. Наприклад:

>>> def this_fails():
...  x = 1/0
... 
>>> try:
...  this_fails()
... except ZeroDivisionError, detail:
...  print 'Відбулася помилка при виконанні:', detail
... 
Відбулася помилка при виконанні: integer division or modulo

Створення винятків

Твердження raise дозволяє програмісту задавати винятки. Наприклад:

>>> raise NameError, 'ПривітВам'
Traceback (most recent call last):
 File "<stdin>", line 1, in ?
NameError: ПривітВам

Перший аргумент для raise називає виняток, що створюється. Необов'язковий другий аргумент є аргументом самого винятку.

Якщо потрібно лише визначити, чи було піднято помилку, без подальшої її обробки, простіша форма твердження raise дозволяє заново підняти помилку:

>>> try:
...  raise NameError, 'ПривітВам'
... except NameError:
...  print 'Тут промайнув виняток!'
...  raise
...
Тут промайнув виняток!
Traceback (most recent call last):
 File "<stdin>", line 2, in ?
NameError: ПривітВам

Винятки, визначені користувачем

Програми можуть визначати нові винятки шляхом створення нового класу винятку. Загалом, винятки повинні походити із класу Exception, прямо чи опосередковано. Наприклад:

>>> class MyError(Exception):
...  def __init__(self, value):
...   self.value = value
...  def __str__(self):
...   return repr(self.value)
... 
>>> try:
...  raise MyError(2*2)
... except MyError, e:
...  print 'Відбувся мій виняток, величина:', e.value
... 
Відбувся мій виняток, величина: 4
>>> raise MyError, 'oops!'
Traceback (most recent call last):
 File "<stdin>", line 1, in ?
__main__.MyError: 'oops!'

Класи винятків (як і звичайні класи) можуть виконуватимуть будь-що, але загалом вони створюються максимально простими і мають обмежену кількість атрибутів, для надання інформації про помилку, яка може використовуватися фільтрами винятків. При створенні модуля, що може відкинути кілька окремих помилок, традиційно створюється  базовий клас для всіх винятків, визначених у цьому модулі, а також підклас для створення специфічних класів винятків для окремих помилок:

class Error(Exception):
 """Базовий клас для винятків цього модуля."""
 pass

class InputError(Error):
 """Винятки для помилок вводу.

 Атрибути:
 expression -- вираз вводу, де відбулася помилка
 message) -- повідомлення про помилку
 """

 def __init__(self, expression, message):
  self.expression = expression
  self.message = message

class TransitionError(Error):
 """Відкидається при недозволеній зміні стану.

 Атрибути:
 previous -- початковий стан переходу
 next -- пропонований наступний стан
 message -- пояснення, чому такий перехід не дозволяється
 """

 def __init__(self, previous, next, message):
  self.previous = previous
  self.next = next
  self.message = message

Більшість винятків мають назви, що закінчуються словом "Error", подібно до назв стандартних винятків.

Багато стандартних модулів визначають свої власні винятки для повідомлень про помилки, що можуть відбутися у визначених ними функціях. Докладніша інформація про класи подана в наступному розділі.

Визначення очищувальних дій

Твердження try ще один необов'язковий блок, який визначає різні "очищувальні" дії, що мають виконатися за будь-яких обставин. Наприклад:

>>> try:
...  raise KeyboardInterrupt
... finally:
...  print 'Прощай, світе!'
... 
Прощай, світе!
Traceback (most recent call last):
 File "<stdin>", line 2, in ?
KeyboardInterrupt

Цей заключний блок виконується незалежно від того, чи відбувся виняток у блоці try . Якщо відбувся виняток, то він заново відкинеться після виконання блоку finally. Фінальний блок також виконується при віході з блоку try за допомогою тверджень break чи return.

Код у заключному блоці корисний для звільнення зовнішніх ресурсів (таких як файли чи зв'язки з мережею), незалежно від того, чи використання цих ресурсів було успішним, чи ні.

Твердження try повинно мати або один чи більше блоків except, або один блок finally, але не обидва разом.