## ++Тема 9++<br>Помилки та винятки. Обробка винятків (c) Яценко Р.М. [Python Advanced] [Навчальний центр комп'ютерних технологій](http://kit.kh.ua/) [![kit](https://i.imgur.com/Kh901c1.png =10%x)](http://kit.kh.ua/) --- ## Навіщо вивчати обробку помилок? ---- ### Реальні ситуації - 🎮 **У грі**: Гравець вводить некоректні дані - 📱 **У додатку**: Немає інтернет-з'єднання - 💾 **При роботі з файлами**: Файл не знайдено - 🔢 **У калькуляторі**: Ділення на нуль **Без обробки помилок програма просто "падає"!** ---- ### Професійний підхід ```mermaid graph LR A[Помилка] --> B{Є обробка?} B -->|Ні| C[💥 Програма зупиняється] B -->|Так| D[✅ Програма працює далі] D --> E[Повідомлення користувачу] E --> F[Можливість виправити] ``` **Обробка винятків = надійність програми** ---- ### Що ви навчитесь - ✅ Розуміти типи помилок - ✅ Передбачати проблемні місця в коді - ✅ Коректно обробляти помилки - ✅ Створювати зрозумілі повідомлення - ✅ Писати надійні програми --- ## 1. Помилки та винятки ---- ### Синтаксичні помилки - **Синтаксичні помилки** порушують синтаксис та пунктуацію мови. Інтерпретатор Python, зустрівши помилковий вираз, не знає, як його інтерпретувати - Зупиняється виконання програми та виводиться відповідне повідомлення із зазначенням місця помилки: ``` >>> 1a = 10 File "<stdin>", line 1 1a = 10 ^ SyntaxError: invalid syntax ``` ---- ### Виняток SyntaxError - У термінології мови Python виник виняток, що належить класу `SyntaxError` - Відповідно до документації Python *синтаксичні помилки* відносять до помилок, а решта (*семантичні помилки*) – до винятків **Запам'ятайте різницю:** - 🔴 **Синтаксична помилка** = порушення правил мови (код не запуститься) - 🟠 **Виняток** = помилка під час виконання (код запустився, але щось пішло не так) ---- ### Виняток NameError Якщо ви спробуєте звернутися до змінної, якій не було надано значення, що у випадку Python означає, що змінна взагалі не була оголошена, вона не існує, то виникне виняток `NameError`: ``` >>> a = 0 >>> print(a + b) Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'b' is not defined ``` **Часта помилка**: друкарські помилки в іменах змінних (`user_name` vs `username`) ---- ### Виняток ValueError У прикладі рядок `'Hi'` не можна перетворити до цілого числа. Виникає виняток `ValueError`, тому що функція `int()` не може перетворити таке значення: ``` >>> int("Hi") Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: invalid literal for int() with base 10: 'Hi' ``` **Коли виникає**: При спробі перетворити некоректне значення до іншого типу ---- ### Виняток TypeError Число `8` і рядок `"3"` належать різним типам, операція додавання між якими не підтримується. При спробі їх скласти виникає виняток `TypeError`: ``` >>> 8 + "3" Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for +: 'int' and 'str' ``` **Рішення**: `8 + int("3")` або `str(8) + "3"` — явне перетворення типів ---- ### Виняток ZeroDivisionError Ділення на нуль викликає виняток `ZeroDivisionError`: ``` >>> 1/0 Traceback (most recent call last): File "<stdin>", line 1, in <module> ZeroDivisionError: division by zero ``` **Математичне правило**: Ділення на нуль невизначено! Python не може обчислити результат ---- ### Інші типи винятків Тип винятку | Опис | Приклад -|-----|----- `IOError` | <small>Помилка введення/виведення</small> | <small>Файл не знайдено</small> `IndexError` | <small>Неіснуючий індекс</small> | <small>`list[100]` у списку з 10 елементів</small> `KeyError` | <small>Неіснуючий ключ у словнику</small> | <small>`dict['age']` коли ключа немає</small> `AttributeError` | <small>Неіснуючий атрибут об'єкта</small> | <small>`"text".append()` — у рядка немає такого методу</small> ---- ### Ієрархія винятків ```mermaid graph TD A[BaseException] --> B[Exception] B --> C[ValueError] B --> D[TypeError] B --> E[ZeroDivisionError] B --> F[NameError] B --> G[IndexError] B --> H[KeyError] ``` **Важливо**: Всі винятки успадковуються від базових класів --- ## 2. Обробка винятків ---- ### Перехоплення винятків - Нерідко дії користувача призводять до того, що у програмі виникає виняток. Наприклад, програма очікує введення числа, але людина ввела букву. Спроба перетворити її до числа призведе до виникнення винятку `ValueError` - На цей випадок у Python існує спеціальний оператор, який дозволяє перехоплювати винятки, що виникають, і обробляти їх **Аналогія**: Як страхувальна сітка в цирку — якщо щось пішло не так, є захист! ---- ### Оператор try-except - У Python перехоплення винятків виконує оператор `try-except`. "Try" перекладається як "спробувати", "except" -- як виняток ```mermaid graph LR A[Початок try] --> B{Виникла помилка?} B -->|Ні| C[Продовжити виконання] B -->|Так| D[Виконати except] D --> C C --> E[Кінець] ``` ``` python try: # блок інструкцій, де може виникнути виняток except: # блок обробки винятку ``` ---- ### Приклад ``` python= n = int(input("Введіть ціле число: ")) print(f"Вдало! Ви ввели число {n}") ``` **💡 Покращена версія**: ```python try: n = int(input("Введіть ціле число: ")) print(f"Вдало! Ви ввели число {n}") except: print("❌ Помилка! Потрібно ввести ціле число") ``` ---- ### Гілка except **Якщо в тілі `try` виняток не виникає, то тіло гілки `except` не виконується** - Ось приклад виведення програми, коли користувач вводить ціле число: ``` Введіть ціле число: 100 Вдало ``` - А тут – коли вводить не те, що очікувалося: ``` Введіть ціле число: AA Щось пішло не так ``` ---- ### Перехоплення певних винятків У тілі `try` можуть виникати різні винятки, і у кожного з них має бути свій обробник. Тому більш правильною є вказівка типу винятку після ключового слова `except`: ``` python= try: n = input('Введіть ціле число: ') n = int(n) print("Все нормально. Ви ввели число", n) except ValueError: print("Ви ввели не ціле число") ``` **Чому це краще?** Різні помилки потребують різної обробки! ---- ### Множинне перехоплення винятків Якщо в тілі `try` виникне ще якийсь виняток, то його не буде оброблено. Для нього треба написати окрему гілку `except`: ``` python= try: a = float(input("Введіть ділене: ")) b = float(input("Введіть дільник: ")) c = a / b print("Результат: %.2f" % c) except ValueError: print("❌ Не можна вводити рядки") except ZeroDivisionError: print("❌ Не можна ділити на нуль") ``` **Порядок важливий**: Перевіряйте від конкретного до загального! ---- ### Групування винятків Декілька винятків можна згрупувати в одну гілку і обробити спільно: ``` python= try: a = float(input("Введіть ділене: ")) b = float(input("Введіть дільник: ")) c = a / b print("Результат: %.2f" % c) except (ValueError, TypeError): print("❌ Помилка вводу даних") except ZeroDivisionError: print("❌ Не можна ділити на нуль") ``` ---- ### Отримання інформації про виняток ```python try: age = int(input("Введіть вік: ")) except ValueError as error: print(f"Помилка: {error}") print(f"Тип помилки: {type(error).__name__}") ``` **Вивід при помилці:** ``` Введіть вік: abc Помилка: invalid literal for int() with base 10: 'abc' Тип помилки: ValueError ``` ---- ### Гілки else та finally ```mermaid graph TD A[try блок] --> B{Помилка?} B -->|Так| C[except блок] B -->|Ні| D[else блок] C --> E[finally блок] D --> E E --> F[Продовження програми] ``` - Блок **`else` спрацює, якщо винятків у `try` не було** - Блок **`finally` виконується завжди** ---- ``` python= try: n = input('Введіть ціле число: ') n = int(n) except ValueError: print("Ви щось наплутали з введенням") else: # виконується, коли в блоці try не виник виняток print("Все нормально. Ви ввели число", n) finally: # виконується у будь-якому випадку print("Кінець програми") ``` ---- ### Приклади виконання ``` Введіть ціле число: 4.3 Ви щось наплутали з введенням Кінець програми Введіть ціле число: 4 Все нормально. Ви ввели число 4 Кінець програми ``` **Використання `finally`**: Закриття файлів, з'єднань з базою даних, звільнення ресурсів --- ## 3. Вкладена обробка ---- ### Обробники для обробників Виняток може виникнути в блоці `except`, `else` або `finally`, і тоді їм потрібен власний обробник: ``` python= try: n = input('Введіть ціле число: ') n = int(n) except ValueError: print("Ви щось наплутали з введенням") 3 / 0 except ZeroDivisionError: print("Ділення на нуль") else: print("Все нормально. Ви ввели число", n) finally: print("Кінець програми") ``` **Проблема**: `ZeroDivisionError` у блоці `except` не буде оброблено! ---- ### Виконання ``` Введіть ціле число: а Ви щось наплутали з введенням Кінець програми Traceback (most recent call last): File "test.py", line 15, in <module> n = int(n) ValueError: invalid literal for int() with base 10: 'а' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "test.py", line 18, in <module> 3 / 0 ZeroDivisionError: division by zero ``` Мало того, що не було оброблено ділення на нуль, сам виняток `ValueError` вважається необробленим ---- ### Рішення проблеми ``` python=4 except ValueError: print("Ви щось наплутали з введенням") try: 3 / 0 except ZeroDivisionError: print("Ділення на нуль") ``` **Правило**: Кожен рівень обробки винятків повинен мати свій блок `try-except` ---- ### Практичний приклад вкладеної обробки ```python= try: filename = input("Назва файлу: ") with open(filename, 'r') as file: try: data = file.read() number = int(data) print(f"Число з файлу: {number}") except ValueError: print("❌ Файл містить не число") except FileNotFoundError: print("❌ Файл не знайдено") ``` **Два рівні**: зовнішній для роботи з файлом, внутрішній для обробки даних --- ## 4. Повний синтаксис try ---- ### try-except-else-finally ``` python= try: # Тут розташовується код, який може викликати виняток [raise Exception("message")] # Інструкція raise генерує виняток # Exception, це один із стандартних типів винятків, може # використовуватись будь-який інший, # у тому числі користувальницький except (Тип винятку1, Тип винятку2, ...) [as Змінна]: # Код у блоці виконується, якщо тип винятку збігається # з одним із типів (Тип винятку1, Тип винятку2, ...) # або є спадкоємцем одного з цих типів. # Отриманий виняток доступний у необов'язковій Змінній ``` ---- ``` python=13 [except (Тип винятку3, Тип винятку4, ...):] # Кількість блоків except не обмежена [raise] # Згенерувати виняток "поверх" отриманого; # без параметрів - повторно згенерувати отримане [except:] # Буде виконано за будь-якого винятку, # не обробленому типізованими блоками except [else:] # Виконується код блоку, якщо не було перехоплено винятків [finally:] # Буде виконано у будь-якому випадку, після відповідного # блоку except або else ``` ---- ### Приклад зі списком ``` python= from random import randint, seed seed() L = [] for j in range(20): L.append(randint(10, 99)) print(L[j], end=' ') print() while True: N = int(input('Введіть значення елемента ' 'для видалення або 0 для виходу\nN = ')) if not N: print('Ви вийшли із програми') break ``` ---- ``` python=16 try: x = L.index(N) except ValueError: print('❌ Такого елемента в списку не існує!') else: L.remove(N) print(f'✅ Елемент у позиції {x} успішно видалено!') finally: print("Поточний список:", end=' ') for it in L: print(it, end=' ') print() ``` ---- ``` 50 58 13 41 53 65 27 29 73 52 31 13 64 18 97 42 95 24 37 60 Введіть значення елемента для видалення або 0 для виходу N = 43 ❌ Такого елемента в списку не існує! Поточний список: 50 58 13 41 53 65 27 29 73 52 31 13 64 18 97 42 95 24 37 60 Введіть значення елемента для видалення або 0 для виходу N = 65 ✅ Елемент у позиції 5 успішно видалено! Поточний список: 50 58 13 41 53 27 29 73 52 31 13 64 18 97 42 95 24 37 60 Введіть значення елемента для видалення або 0 для виходу N = 0 Ви вийшли із програми ``` --- ## 5. Оператор assert <!-- https://pythonist.ru/assert-v-python/ --> ---- ### Призначення - Оператор `assert` - це оператор Python, який використовується для налагодження коду - Працює як логічний вираз, перевіряючи, чи є задана умова істинною або хибною: - Якщо істинно, то нічого не відбувається і виконується наступний рядок коду - Якщо хибно, то зупиняє виконання програми та видає `AssertionError` **Коли використовувати**: Під час розробки для перевірки припущень (не для обробки помилок користувача!) ---- ### Синтаксис - Синтаксис використання оператора `assert` наступний: ```python assert <condition> ``` - Можна додати додаткове повідомлення, яке буде виводитись, якщо умова помилкова: ```python assert <condition>, <message> ``` **Аналогія**: Це як автоматична перевірка на контрольній роботі — якщо щось не так, одразу помітите! ---- ### Приклад ```python= num1 = 10 num2 = int(input()) assert num2 != 0, "Дільник дорівнює нулю" print("Результат:", num1/num2) ``` ``` 0 Traceback (most recent call last): File "main.py", line 3, in <module> AssertionError: Дільник дорівнює нулю 2 Результат: 5.0 ``` ---- ### Практичне застосування assert ```python def calculate_discount(price, discount_percent): assert 0 <= discount_percent <= 100, "Знижка має бути 0-100%" assert price > 0, "Ціна має бути додатною" return price * (1 - discount_percent / 100) # Використання try: result = calculate_discount(100, 150) # Помилка! except AssertionError as e: print(f"Помилка у функції: {e}") ``` **Важливо**: `assert` можна вимкнути при запуску Python з параметром `-O` (оптимізація) --- ## 6. Створення власних винятків ---- ### Навіщо потрібні власні винятки? Іноді стандартних винятків недостатньо для опису специфічних помилок у вашій програмі. ```python class NegativeAgeError(Exception): """Виняток для від'ємного віку""" pass class TooOldError(Exception): """Виняток для занадто великого віку""" pass def set_age(age): if age < 0: raise NegativeAgeError("Вік не може бути від'ємним!") if age > 150: raise TooOldError("Вік занадто великий!") return age ``` ---- ### Використання власних винятків ```python try: user_age = int(input("Ваш вік: ")) age = set_age(user_age) print(f"Ваш вік: {age}") except NegativeAgeError as e: print(f"❌ {e}") except TooOldError as e: print(f"❌ {e}") except ValueError: print("❌ Введіть число!") ``` --- ## Домашнє завдання 1. Набрати та запустити на виконання усі приклади програм з лекції 2. Створити таблицю з 5 типами винятків, які ви зустріли в своїх програмах (c) Яценко Р.М., 2019-2026 [Навчальний центр комп'ютерних технологій](http://kit.kh.ua/) [![kit](https://i.imgur.com/Kh901c1.png =10%x)](http://kit.kh.ua/)
{"metaMigratedAt":"2023-06-18T02:23:14.650Z","metaMigratedFrom":"YAML","breaks":false,"slideOptions":"{\"allottedMinutes\":80,\"transition\":\"slide\",\"theme\":\"beige\",\"slideNumber\":\"c\",\"spotlight\":{\"enabled\":true}}","title":"Тема 9. Помилки та винятки. Обробка винятків","description":"© Яценко Р.М., 2019-2023","contributors":"[{\"id\":\"93a8c43f-1b5b-4461-9101-89b183ccbc1c\",\"add\":30876,\"del\":15410,\"latestUpdatedAt\":1768055342164}]"}
    626 views