Винятки#

uk-flagанглійська: Exceptions

Винятки трапляються, коли у вашій програмі виникають виняткові (exceptional) ситуації. Наприклад,якщо ви збираєтеся прочитати файл, а файл не існує? Або, якщо ви випадково видалили файл під час роботи програми? Такі ситуації обробляються за допомогою винятків (англ.“exceptions”).

Подібним чином, якби ваша програма мала деякі неприпустимі команди? У цьому випадку Python піднімає (англ.“raises”)руки та повідомляє, що виявив помилку (англ.“error”).

Помилки#

uk-flagанглійська: Errors

Розглянемо простий виклик функції print. Що, якщо ми помилково напишемо print як Print? Зверніть увагу на використання великих літер (англ.“capitalization”). У цьому випадку Python піднімає синтаксичну помилку.

Приклад англійською:

>>> Print("Hello World")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'Print' is not defined
>>> print("Hello World")
Hello World

Приклад українською (скріншот, зроблений в оболонці Python за допомогою IDLE): screenshot_erros.png

де: name ‘Print’ is not defined.Did you mean:‘print’? - ім’я «Print» не визначено. Ви мали на увазі :‘print’?

Зверніть увагу, що була підіймана помилкаNameError,а також друкується місце, де було виявлено помилку. Так у цьому випадку діє обробник помилок (англ.“error handler”).

Винятки#

uk-flagанглійська: Exceptions

Ми спробуємо (англ.“try”) прочитати щось від користувача. Введіть перший рядок нижче та натисніть клавішу Enter. Коли ваш комп’ютер запропонує вам ввести дані, натомість натисніть [ctrl-d] на Mac або [ctrl-z] на Windows і подивіться, що станеться. (Якщо ви користуєтеся Windows і жоден із варіантів не працює, ви можете спробувати [ctrl-c] у командному рядку, тобто створити KeyboardInterrupt error).

Приклад англійською, автор використувує [ctrl-d] чи [ctrl-z]:

>>> s = input('Enter something --> ')
Enter something --> Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
EOFError

Python підіймає помилку під назвою end-of-file (EOFError)(для користувачів,які використувують [ctrl-d] чи [ctrl-z]), яка в основному означає, що він знайшов символ кінця файлу.

Приклад українською ( скріншот [ctrl-c], зроблений в оболонці Python за допомогою IDLE): screenshot_keyboarddinterrupt.png

Python підіймає помилку під назвою KeyboardInterrupt Error(для користувачів,які використувують [ctrl-c]).

І в англійському, і в українському варіанті сталася помилка.

Обробка винятків#

uk-flagанглійська: Handling Exceptions

Ми можемо обробляти винятки за допомогою оператора try..except. По суті, ми розміщуємо наші звичайні команди в блоці try, а всі наші обробники винятків помилок – у блоці except.

код python exceptions_handle_ukr.py

try:
    text = input('Введіть щось --> ')
except EOFError:
    print('Чому ви прислалі мені символ кінця файлу?')
except KeyboardInterrupt:
    print('Ви скасували операцію.')
else:
    print('Ви увійшли{}'.format(text))

Висновок:

$ python exceptions_handle_ukr.py
Введіть щось -->   # натисніть ctrl + d
Чому ви прислалі мені сигнал кінец файлу? 

$ python exceptions_handle_ukr.py
Введіть щось -->  # натисніть ctrl + c
Ви скасували операцію.  

$ python exceptions_handle_ukr.py
Введіть щось --> Без винятків
Ви увійшли Без винятків

Підказка: якщо CTRL+c закриває вікно терміналу замість того, щоб створити очікуване повідомлення про помилку, спробуйте написати цю програму за допомогою IDLE (Меню: File -> New File, потім натисніть Run -> Run module) screenshot_exceptions_handle.png

Як це працює

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

Зауважте, що для кожного виразу try має бути принаймні одне речення except. Інакше який сенс мати блок try?

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

Також можна додати пункт else до відповідного блоку try..except. Пункт else виконується, якщо не відбувається винятків.

У наступному прикладі ми також побачимо, як отримати об’єкт винятку, щоб ми могли отримати додаткову інформацію.

Виклик винятків#

uk-flagанглійська: Raising Exceptions

Ви можете викликати винятки за допомогою оператора raise, передавши йому ім’я помилки або винятку, а також об’єкт винятку, який потрібно викинути.

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

код python exceptions_raise_ukr.py:

class Виняток_короткого_введення(Exception):
    '''Визначений користувачем клас винятків.'''
    def __init__(self, довжина, як_мінімум):
        Exception.__init__(self)
        self.довжина = довжина
        self.як_мінімум = як_мінімум

try:
    текст = input('Введіть щось --> ')
    if len(текст) < 3:
        raise Виняток_короткого_введення(len(текст), 3)
    # Тут може відбуватися звичайна робота
except EOFError:
    print('Чому ви прислалі мені символ кінця файлу?')
except Виняток_короткого_введенн as вн:
    print(('Виняток_короткого_введення: Довжина_введеного_рядка ' +
           '{0} очікувалося_як_мінімум {1}')
          .format(вн.довжина, вн.як_мінімум))
else:
    print('Винятків не було.')

Висновок:

$ python exceptions_raise_ukr.py
Введіть щось --> a
Виняток_короткого_введення: Довжина_введеного_рядка очікувалося_як_мінімум 3

$ python exceptions_raise_ukr.py
Введіть щось --> abc
Винятків не було.

python code exceptions_raise_en.py:

class ShortInputException(Exception):
    '''A user-defined exception class.'''
    def __init__(self, length, atleast):
        Exception.__init__(self)
        self.length = length
        self.atleast = atleast

try:
    text = input('Enter something --> ')
    if len(text) < 3:
        raise ShortInputException(len(text), 3)
    # Other work can continue as usual here
except EOFError:
    print('Why did you do an EOF on me?')
except ShortInputException as ex:
    print(('ShortInputException: The input was ' +
           '{0} long, expected at least {1}')
          .format(ex.length, ex.atleast))
else:
    print('No exception was raised.')

output:

$ python exceptions_raise_en.py
Enter something --> a
ShortInputException: The input was 1 long, expected at least 3

$ python exceptions_raise_en.py
Enter something --> abc
No exception was raised.

Як це працює

Тут ми створюємо власний тип винятку. Цей новий тип винятку називається Виняток_короткого_введення. У ньому є два поля: довжина, що зберігає довжину введеного тексту, і як_мінімум, що вказує, яку мінімальну довжину тексту очікувала програма.

У пункті except ми вказуємо клас помилки, який зберігатиметься як(англ.as) змінна вн , що містить відповідний об’єкт помилки/виключення. Це аналогічно параметрам і аргументам у виклику функції. Всередині цього пункту except ми використовуємо поля довжину і як_мінімум об’єкта винятку, щоб надрукувати відповідне повідомлення для користувача.

Try … Finally#

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

код python exceptions_finally_ukr.py:

import sys
import time

f = None
try:
    f = open("вірш.txt")
    # наш звичайний спосіб читати файли
    while True:
        лінія = f.readline()
        if len(лінія) == 0:
            break
        print(лінія, end='')
        sys.stdout.flush()
        print("Натисніть ctrl+c зараз")
        # Щоб переконатися, що він працює деякий час
        time.sleep(2)
except IOError:
    print("Не вдалося знайти файл вірш.txt")
except Клавіатура_переривання:
    print("!! Ви скасували читання з файлу.")
finally:
    if f:
        f.close()
    print("(Очищення: файл закрито)")

Висновок:

$ python exceptions_finally_ukr.py
Програмування – це весело.
Натисніть ctrl+c зараз

^C!! Ви скасували читання з файлу.
(Очищення: файл закрито)

python code exceptions_finally_en.py:

import sys
import time

f = None
try:
    f = open("poem.txt")
    # Our usual file-reading idiom
    while True:
        line = f.readline()
        if len(line) == 0:
            break
        print(line, end='')
        sys.stdout.flush()
        print("Press ctrl+c now")
        # To make sure it runs for a while
        time.sleep(2)
except IOError:
    print("Could not find file poem.txt")
except KeyboardInterrupt:
    print("!! You cancelled the reading from the file.")
finally:
    if f:
        f.close()
    print("(Cleaning up: Closed the file)")

output:

$ python exceptions_finally.py
Programming is fun
Press ctrl+c now
^C!! You cancelled the reading from the file.
(Cleaning up: Closed the file)

Як це працює

Ми виконуємо звичайне читання файлів, але ми довільно ввели сплячий режим протягом 2 секунд після друку кожного рядка за допомогою функції time.sleep, щоб програма працювала повільно (Python дуже швидкий за своєю природою). Коли програма все ще працює, натисніть ctrl + c, щоб перервати/скасувати програму.

Зверніть увагу, що виникає виняток Клавіатура_переривання і програма завершує роботу. Однак перед завершенням роботи програми виконується пункт finally, і файловий об’єкт завжди закривається.

Зауважте, що змінна, якій присвоєно значення 0 або None, або змінна, яка є порожньою послідовністю чи колекцією, вважається False у Python. Ось чому ми можемо використовувати if f: у коді вище.

Також зауважте, що ми використовуємо sys.stdout.flush() після print, щоб він негайно друкувався на екрані.

Оператор with#

uk-flagанглійська: The with statement

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

Зберегти якexceptions_using_with.py:

код python exceptions_using_with_ukr.py:

with open("вірш.txt") as f:
    for лінія in f:
        print(лінія, end='')

Як це працює

Результат має бути таким самим, як у попередньому прикладі. Різниця тут полягає в тому, що ми використовуємо функцію open з оператором with - цим ми залишаємо автоматичне закриття файлу під відповідальність with open.

Те, що відбувається за кулісами, полягає в тому, що існує такий собі протокол, який використовується оператором with. Він зчитує об’єкт, який повертається оператором open, назвемо його в даному випадку “thefile”.

Перед запуском блоку коду, що міститься в ньому, оператор with завжди викликає функцію із файлуthefile.__enter__,а також завжди викликає thefile.__exit__ після завершення цього блоку кода.

Таким чином, код, який ми б написали в блоці finally, повинен автоматично оброблятися методом __exit__. Це те, що допомагає нам уникнути повторного використання явних операторів try..finally.

Додаткове обговорення цієї теми виходить за рамки цієї книги, тому, будь ласка, зверніться до PEP 343 для вичерпного пояснення.

Що робить оператор with (від перекладача):#

Кожен файловий об’єкт має дві функції: enter та exit. Оператор with спочатку викликає функцію enter об’єкта файлу, потім виконує всі рядки коду всередині блоку with, а після завершення викликає функцію exit цього ж об’єкта.

Без блоку with вам потрібно написати код так:

код python poem_ukr.py:

f = open("вірш.txt")
# ще кілька команд Python
f.close()
# інший код

python code poem_en.py

f = open("poem.txt")
# some more python commands
f.close()
# other code

Використовуючи блок with, ви можете писати елегантніше і не турбуватись про закриття файлу:

код python poem2_ukr.py:

with open("вірш.txt") as f:
     print("файл зараз відкрит")
# ще кілька команд Python
# тепер автоматично закривається!
print("файл зараз закрит")
# інший код

python code poem2_en.py

with open("poem.txt") as f:
    print("file is now open")
    # some more python commands
# now automatically closed!
print("file is now closed")
# other code

Резюме#

Ми обговорили використання операторів try..except і try..finally. Ми побачили, як створювати власні типи винятків, а також як викликати винятки.

Далі ми вивчимо стандартну бібліотеку Python.