Skip to content

Exceptions

Exceptions - Винятки

Виняток (Exception)

Summary

Виняток (exception) - це помилки Python, виявлена під час виконання коду.

Обробка виняткових ситуацій або обробка винятків (exception handling) - механізм мов програмування, призначений для опису реакції програми на помилки під час виконання та інші можливі проблеми (винятки), які можуть виникнути під час виконання програми та призводити до неможливості (безглуздості) подальшого виконання основного алгоритму програми.

Код на Python може створити виняток за допомогою ключового слова raise. Після нього вказується об'єкт винятку. Також можна вказати клас винятку, в такому випадку буде автоматично викликаний конструктор без параметрів. raise може викидати в якості винятків лише екземпляри класу BaseException та його нащадків.

Блоки except обробляються зверху донизу, і управління передається не більш як одному обробнику. Тому, якщо потрібно обробити винятки, які знаходяться в ієрархії успадкування, по-різному, спочатку слід вказувати обробники менш загальних винятків, а потім - більш загальних. Також саме тому порожній except може бути лише останнім (інакше SyntaxError). Більш того, якщо спочатку розташувати обробники більш загальних винятків, то обробники менш загальних будуть просто проігноровані.

Як обробити виняток?

Для обробки винятків у Python використовується конструкція try...except...finally.

try:    
    result = 10 / 0  # Code that may raise an exception
except ZeroDivisionError:    
    print("Error: Division by zero!")  # Handling a specific type of exception
except Exception as e   
    print("An error occurred:", e)   # Handling other types of exceptions
else:    
    print("Division was successful!")  # Executed if no exception occurred
finally:    
    print("Finishing the processing")  # Executed regardless of whether an exception occurred

У цьому прикладі конструкція try...except дозволяє спробувати виконати код, в якому може виникнути виняток. Якщо виняток виникає, програма переходить до відповідного блоку except, який обробляє виняток певного типу. Якщо потрібно обробити винятки, які знаходяться в ієрархії успадкування, по-різному, спочатку слід вказувати обробники менш загальних винятків, а потім - більш загальних. У разі, якщо тип винятку не відповідає жодному блоку except, виняток може бути оброблений в блоку except Exception.

Блок else виконується, якщо виняток не виник, тобто код у try виконався успішно.

Блок finally виконується завжди, незалежно від того, чи виникла помилка чи ні. Він часто використовується для звільнення ресурсів або виконання завершальних операцій. Блок finally буде виконаний навіть якщо в блоці try є оператор return. Це одна з особливостей конструкції try...finally в Python. Навіть якщо в блоці try виконується return, блок finally все одно буде виконаний перед тим, як функція поверне значення. Це зручно використовувати для завершення сесії бази даних або закриття файлу.

Що станеться, якщо помилку не обробить блок except

Якщо жоден із заданих блоків except не перехоплює виняток, то він буде перехоплений найближчим зовнішнім блоком try/except, в якому є відповідний обробник. Якщо ж програма не перехоплює виняток зовсім, то інтерпретатор завершує виконання програми і виводить інформацію про виняток до стандартного потоку помилок sys.stderr. Є два винятки з цього правила: - Якщо виняток виникає в деструкторі об'єкта, виконання програми не завершується, а до стандартного потоку помилок виводиться попередження "Exception ignored" з інформацією про виняток. - При виникненні винятку SystemExit відбувається лише завершення програми без виводу інформації про виняток на екран (не стосується попереднього пункту, в деструкторі поведінка цього винятку буде такою ж, як і у інших).

Що робити, якщо потрібно перехопити виняток, виконати дії і знову викликати цей самий виняток

Для того, щоб в обробнику винятку виконати певні дії, а потім передати виняток далі на один рівень вище (тобто, знову викинути той самий виняток), використовується ключове слово raise без параметрів.

try:
    1 / 0
except ZeroDivisionError:
    ...
    raise

Для чого може використовуватися конструкція try...finally без except

Якщо в блоці try виникне помилка, то блок finally все одно буде виконаний, і всередині нього можна зробити "cleanup" (наприклад).

try:
    ...
finally:
    ...

Що таке ланцюжок винятків

У Python 3 при виникненні винятку в блоку except, попередній виняток зберігається в атрибуті __context__ і якщо новий виняток не обробляється, буде виведена інформація про те, що новий виняток виник при обробці попереднього («During handling of the above exception, another exception occurred:»).

Також можна зв'язувати винятки в один ланцюжок або замінювати старі новими. Для цього використовується конструкція raise новий_виняток from старий_виняток або raise новий_виняток from None.

У першому випадку вказаний виняток зберігається в атрибуті __cause__, а атрибут __suppress_context__ (який приховує виведення винятку з __context__) встановлюється в False. Тоді, якщо новий виняток не обробляється, буде виведена інформація про те, що старий виняток є причиною нового («The above exception was the direct cause of the following exception:»).

У другому випадку __suppress_context__ встановлюється в True, а __cause__ - в None. Тоді при виведенні винятку воно фактично буде замінено новим (хоча старий виняток все ще зберігається в __context__).

Для чого потрібен блок else

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

Що можна передати у конструктор винятку

Винятки можуть приймати будь-які безіменні аргументи у конструкторі. Вони розміщуються в атрибуті args у вигляді кортежу (не змінюваного списку). Найчастіше використовується один рядковий параметр, який містить повідомлення про помилку. У всіх винятках визначений метод __str__, який за замовчуванням викликає str(self.args).

Exception Object (Об'єкт винятку)

arguments = <name>.args
exc_type  = <name>.__class__
filename  = <name>.__traceback__.tb_frame.f_code.co_filename
func_name = <name>.__traceback__.tb_frame.f_code.co_name
line      = linecache.getline(filename, <name>.__traceback__.tb_lineno)
trace_str = ''.join(traceback.format_tb(<name>.__traceback__))
error_msg = ''.join(traceback.format_exception(exc_type, <name>, <name>.__traceback__))

Класи винятків

  • BaseException - базовий клас для всіх винятків.
  • Системні винятки
    • SystemExit - виняток, який генерується функцією sys.exit(). Використовується для завершення роботи програми.
    • KeyboardInterrupt - завершення програми шляхом натискання Ctrl+C в консолі.
  • Exception - клас-нащадок BaseException, базовий клас для всіх стандартних винятків, які не вказують на обов'язкове завершення програми, і для всіх користувацьких винятків.

Потомки Exception

  • SyntaxError - помилка синтаксису.
    • IndentationError - неправильний відступ.
      • TabError - змішане використання символів табуляції і пробілів.
  • ArithmeticError - базовий клас для всіх винятків, пов'язаних з арифметичними операціями.
    • ZeroDivisionError - ділення на нуль.
    • FloatingPointError - помилка операції над числами з рухомою комою.
    • OverflowError - результат арифметичної операції занадто великий, щоб бути представленим.
  • AssertionError - невиконання умови в операторі assert.
  • AttributeError - помилка доступу до атрибуту.
  • EOFError - піднімається функцією input(), коли вона зустрічає умову завершення файлу (end-of-file condition).
  • LookupError - базовий клас для винятків, коли в колекції не знайдено елемент.
    • IndexError - неправильний індекс послідовності (наприклад, списку).
    • KeyError - відсутній ключ у словнику
  • MemoryError - недостатньо пам'яті.
  • NameError - ім'я не знайдено.
    • UnboundLocalError - локальне ім'я використовується перед тим, як воно буде визначено.
  • OSError - системна помилка. Помилки, такі як FileExistsError/PermissionError
  • RuntimeError - загальна помилка виконання, яка не належить до жодної з категорій.
    • RecursionError - коли досягнута максимальна глибина рекурсії.
    • NotImplementedError - дія не реалізована. Призначено, серед іншого, для створення абстрактних методів.
  • StopIteration - піднімається функцією next(), коли вона виконується на пустому ітераторі.
  • TypeError - помилка несумісності типів даних, над якими виконується операція.
  • ValueError - генерується, коли функції або операції передано об'єкт правильного типу, але з неправильним значенням, причому цю ситуацію неможливо описати більш точним винятком, таким як IndexError. Наприклад, спроба перетворити на число рядок, який не може бути числом.
    • UnicodeError - генерується при невдалому кодуванні/декодуванні рядків в байти та навпаки.
  • BufferError - базовий клас для винятків, пов'язаних з операціями над буфером.
  • ImportError - помилка імпорту модуля або імені з модуля.
  • SystemError - некритична внутрішня помилка інтерпретатора. При виникненні цього винятка слід залишити звіт про помилку на сайті bugs.python.org

В яких випадках можна обробити SyntaxError

Помилка синтаксису виникає, коли синтаксичний аналізатор Python зіштовхується з частиною коду, який не відповідає специфікації мови і не може бути інтерпретований.

Оскільки у випадку синтаксичної помилки у головному модулі вона виникає до початку виконання програми і не може бути перехоплена, посібник для початківців у документації мови Python навіть розділяє синтаксичні помилки та виключення. Проте SyntaxError - це також виключення, яке успадковується від класу Exception, і є ситуації, коли воно може виникнути під час виконання та бути оброблене, а саме:

  • помилка синтаксису в імпортованому модулі;
  • помилка синтаксису в коді, який представляється рядком і передається функції eval або exec.

Чи можна створювати власні виключення

Можна. Вони повинні успадковувати клас Exception. Зазвичай назви виключень закінчуються словом Error.

Для чого потрібні попередження (warnings) і як створити власне

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

Базовим класом для попереджень є Warning, який успадковується від Exception. Базовим класом-нащадком Warning для користувацьких попереджень є UserWarning.

Для чого потрібний модуль warning

У модулі warning зібрані функції для роботи з попередженнями.

Основною є функція warn, яка приймає один обов'язковий параметр message, який може бути або рядком-повідомленням, або екземпляром класу або підкласу Warning (у такому випадку параметр category встановлюється автоматично), а також два необов'язкових параметра: category (за замовчуванням - UserWarning) - клас попередження і stacklevel (за замовчуванням - 1) - рівень вкладеності функцій, починаючи з якого необхідно виводити вміст стеку викликів (корисно, наприклад, для функцій-обгорток для виведення попереджень,де слід задати stacklevel=2, щоб попередження стосувалося місця виклику даної функції, а не самої функції).