2.25. Обробка помилок

Більшість помилок часу виконання в Python виявляються як винятки – іменований, структурований спосіб повідомити, що щось пішло не так. ValueError, TypeError, KeyError, OSError, MemoryError – усі це приклади; кожен є класом, і виклик одного з них зупиняє поточний виклик та шукає обробник у навколишньому коді.

2.25.1. try / except

Загорніть блок коду в try, щоб перехопити будь-який виняток, який він може викликати:

try:
    n = int(input_text)
except ValueError:
    n = 0

Якщо перетворення int(...) не вдасться, керування переходить до блоку except замість подальшого поширення помилки. Якщо input_text містив коректний цілочисельний рядок, блок except пропускається.

Один блок try може мати кілька блоків except, кожен з яких перехоплює інший вид помилки:

try:
    value = data[key]
except KeyError:
    value = None
except TypeError:
    value = -1

Python перевіряє їх по черзі; перший, клас винятку якого підходить, обробляє проблему. Перехоплення Exception (базового класу для майже всього) обробляє будь-яку помилку; резервуйте це для найзовнішнього шару програми, де альтернатива – аварійне завершення.

Попередження

Голий except: (без класу після ключового слова) також перехоплює KeyboardInterrupt – виняток, який IDE надсилає при натисканні кнопки зупинити для переривання запущеного скрипта. Цикл, загорнутий у голий except: pass, поглине переривання та продовжить виконання, не залишивши способу зупинити скрипт, крім перезапуску живлення.

Надавайте перевагу except Exception: перед голим except:, коли вам дійсно потрібно широке перехоплення. KeyboardInterrupt успадковується від BaseException, а не від Exception, тому except Exception: залишає кнопку зупинки робочою.

2.25.1.1. Перегляд винятку

Щоб прочитати повідомлення, прикріплене до винятку, назвіть його через as:

try:
    f = open("data.txt")
except OSError as e:
    print("could not open file:", e)

Змінна, прив’язана через as, дійсна лише всередині блоку except.

2.25.2. else та finally

Блок try має два необов’язкові розширення.

else виконується лише тоді, коли try завершився без виклику винятку:

try:
    value = compute()
except ValueError:
    print("bad input")
else:
    print("got", value)

Розміщення «що робити, якщо все пройшло добре» в else звужує блок try – лише рядок, який може не вдатися, належить у try.

finally виконується в кінці в будь-якому випадку – незалежно від того, чи try завершився успішно, чи викликав виняток, який було перехоплено, чи викликав виняток, який поширюється далі:

try:
    do_work()
finally:
    cleanup()
Flow diagram showing the four possible paths through try / except / else / finally: success goes to else then finally; a caught exception goes to except then finally; an uncaught exception goes to finally then propagates.

finally виконується завжди. else виконується лише на шляху без винятку.

Для більшості шаблонів «отримати/звільнити» надавайте перевагу контекстному менеджеру перед парою try / finally – ресурс сам керує своїм очищенням.

2.25.3. Поширені вбудовані винятки

Короткий перелік винятків, з якими ви будете стикатися часто:

  • ValueError – правильний тип, неправильне значення (bytes([300]) – 300 має правильний тип, але виходить за межі допустимого діапазону байтів 0..255).

  • TypeError – зовсім неправильний тип (len(42)).

  • KeyError – відсутній ключ у dict.

  • IndexError – індекс за межами послідовності.

  • AttributeError – звернення до атрибута, якого не існує ("abc".foo).

  • OSError – помилка файлової системи або введення/виведення.

  • MemoryError – вичерпано купу. На середовищі з обмеженою пам’яттю це може відбуватись під час звичайної роботи – не лише в патологічних випадках.