## ++Тема 9++<br>Помилки та винятки. Обробка винятків
(c) Яценко Р.М. [Python Advanced]
[Навчальний центр комп'ютерних технологій](http://kit.kh.ua/) [](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/) [](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}]"}