Python · Синтаксис · Початковий рівень
Основи обробки помилок
Навчіться обробляти помилки за допомогою try/except/else/finally, викидати власні виключення та писати надійний Python-код.
Короткий вступ до теми та пояснення перед вправами (вправи нижче):
Патерни обробки помилок
#Довідник: обробка помилок
#Вправи:
Безпечне перетворення у int
#Напишіть функцію `safe_int(s, default=0)`, яка намагається перетворити `s` на ціле число і повертає його при успіху, або `default` якщо перетворення неможливе. Ніколи не повинна кидати виключення. Приклад: `safe_int("42")` → `42`, `safe_int("abc")` → `0`, `safe_int("abc", -1)` → `-1`.
def safe_int(s, default=0):
pass
print(safe_int("42")) # 42
print(safe_int("abc")) # 0
print(safe_int("abc", -1)) # -1
Рішення
def safe_int(s, default=0):
try:
return int(s)
except (ValueError, TypeError):
return default
print(safe_int("42")) # 42
print(safe_int("abc")) # 0
print(safe_int("abc", -1)) # -1
Безпечне ділення
#Напишіть функцію `safe_divide(a, b)`, яка повертає `a / b` як float, або `None` якщо `b` дорівнює нулю. Використовуйте try/except. Приклад: `safe_divide(10, 2)` → `5.0`, `safe_divide(10, 0)` → `None`.
def safe_divide(a, b):
pass
print(safe_divide(10, 2)) # 5.0
print(safe_divide(10, 0)) # None
Рішення
def safe_divide(a, b):
try:
return a / b
except ZeroDivisionError:
return None
print(safe_divide(10, 2)) # 5.0
print(safe_divide(10, 0)) # None
Кілька типів виключень
#Напишіть функцію `parse_value(s)`, яка: - Повертає `int(s)` якщо `s` можна перетворити на ціле - Повертає `float(s)` якщо можна на float, але не на int - Повертає `None` якщо жодне перетворення не спрацьовує Не використовуйте `.` для визначення float — тільки try/except.
def parse_value(s):
pass
print(parse_value("42")) # 42 (int)
print(parse_value("3.14")) # 3.14 (float)
print(parse_value("abc")) # None
Рішення
def parse_value(s):
try:
return int(s)
except ValueError:
pass
try:
return float(s)
except ValueError:
return None
print(parse_value("42")) # 42
print(parse_value("3.14")) # 3.14
print(parse_value("abc")) # None
Валідація віку
#Напишіть функцію `validate_age(age)`, яка: - Кидає `TypeError` якщо `age` не є цілим числом - Кидає `ValueError` з повідомленням `"Age must be between 0 and 150"` якщо вік поза цим діапазоном - Повертає `age` якщо валідний Потім напишіть код, що викликає `validate_age` з введенням користувача і обробляє обидва виключення з відповідними повідомленнями.
def validate_age(age):
pass
print(validate_age(25)) # 25
try:
validate_age("old")
except TypeError as e:
print(e)
try:
validate_age(200)
except ValueError as e:
print(e)
Рішення
def validate_age(age):
if not isinstance(age, int):
raise TypeError(f"Expected int, got {type(age).__name__}")
if not 0 <= age <= 150:
raise ValueError("Age must be between 0 and 150")
return age
print(validate_age(25)) # 25
try:
validate_age("old")
except TypeError as e:
print(e)
try:
validate_age(200)
except ValueError as e:
print(e)
Використання else
#Напишіть функцію `read_number(s)`, яка: - Намагається перетворити `s` у float - Якщо не вдається — виводить `"Invalid input"` і повертає `None` - Якщо вдається (використайте `else`) — виводить `"Got: {value:.2f}"` і повертає float Виведення у випадку успіху має бути всередині блоку `else`, а не всередині `try`.
def read_number(s):
pass
print(read_number("3.14")) # виводить "Got: 3.14", повертає 3.14
print(read_number("abc")) # виводить "Invalid input", повертає None
Рішення
def read_number(s):
try:
value = float(s)
except ValueError:
print("Invalid input")
return None
else:
print(f"Got: {value:.2f}")
return value
print(read_number("3.14")) # Got: 3.14
print(read_number("abc")) # Invalid input
Очищення у finally
#Напишіть функцію `safe_open(path)`, яка: - Відкриває файл за `path` і читає його вміст - Повертає вміст як рядок при успіху - Повертає порожній рядок якщо виникає `FileNotFoundError` - Завжди виводить `"Closing file"` у блоці `finally`, незалежно від того чи вдалось відкрити файл (Можна симулювати відкриття через змінну яка може бути None.)
def safe_open(path):
pass
print(safe_open("/tmp/test_eh.txt")) # вміст або ""
print(safe_open("/tmp/no_such_file.txt")) # ""
Рішення
def safe_open(path):
f = None
try:
f = open(path, encoding="utf-8")
return f.read()
except FileNotFoundError:
return ""
finally:
print("Closing file")
if f:
f.close()
import pathlib
pathlib.Path("/tmp/test_eh.txt").write_text("hello", encoding="utf-8")
print(safe_open("/tmp/test_eh.txt"))
print(safe_open("/tmp/no_such_file.txt"))
Безпечний доступ до вкладеного словника
#Напишіть функцію `get_user_city(data, username)`, яка отримує `data[username]["address"]["city"]`. Обробіть `KeyError` якщо будь-який ключ відсутній і повертайте `"Unknown"` у такому випадку. Приклад: `get_user_city({"alice": {"address": {"city": "Kyiv"}}}, "alice")` → `"Kyiv"`, `get_user_city({}, "bob")` → `"Unknown"`.
def get_user_city(data, username):
pass
users = {"alice": {"address": {"city": "Kyiv"}}}
print(get_user_city(users, "alice")) # Kyiv
print(get_user_city(users, "bob")) # Unknown
Рішення
def get_user_city(data, username):
try:
return data[username]["address"]["city"]
except KeyError:
return "Unknown"
users = {"alice": {"address": {"city": "Kyiv"}}}
print(get_user_city(users, "alice")) # Kyiv
print(get_user_city(users, "bob")) # Unknown
Raise з валідацією
#Напишіть функцію `create_username(name)`, яка: - Кидає `ValueError` якщо `name` порожній або містить тільки пробіли - Кидає `ValueError` якщо `name` довший за 20 символів - Кидає `ValueError` якщо `name` містить пробіли - Інакше повертає `name.lower().strip()` Додайте зрозуміле повідомлення про помилку в кожен `ValueError`.
def create_username(name):
pass
print(create_username("Alice")) # alice
print(create_username(" Bob ")) # bob
try:
create_username("")
except ValueError as e:
print(e)
Рішення
def create_username(name):
name = name.strip()
if not name:
raise ValueError("Username cannot be empty")
if len(name) > 20:
raise ValueError("Username must be 20 characters or less")
if " " in name:
raise ValueError("Username cannot contain spaces")
return name.lower()
print(create_username("Alice")) # alice
print(create_username(" Bob ")) # bob
try:
create_username("")
except ValueError as e:
print(e)
Ланцюжок операцій
#Напишіть функцію `process(text)`, яка виконує три операції послідовно: 1. Перетворює `text` у верхній регістр 2. Розбиває за комою і бере перший елемент 3. Перетворює перший елемент у ціле число Обробіть `ValueError`, `IndexError` та `AttributeError` в одній гілці `except` і повертайте `None` при будь-якій помилці. При успіху повертайте ціле число.
def process(text):
pass
print(process("42,extra")) # 42
print(process("no-comma")) # None
print(process(None)) # None
Рішення
def process(text):
try:
upper = text.upper()
first = upper.split(",")[0]
return int(first)
except (ValueError, IndexError, AttributeError):
return None
print(process("42,extra")) # 42
print(process("no-comma")) # None
print(process(None)) # None
Звіт про виключення
#Напишіть функцію `run_all(functions, input_value)`, яка приймає список функцій і запускає кожну з `input_value`. Повертає список результатів, де кожен елемент або повернене значення (якщо функція успішна), або рядок `"ERROR: {exception_message}"` якщо виникло виключення. Приклад: з `functions = [str, int, lambda x: x/0]` і `input_value = "42"`, результат: `["42", 42, "ERROR: division by zero"]`.
def run_all(functions, input_value):
pass
funcs = [str, int, lambda x: x / 0]
print(run_all(funcs, "42"))
# ["42", 42, "ERROR: division by zero"]
Рішення
def run_all(functions, input_value):
results = []
for fn in functions:
try:
results.append(fn(input_value))
except Exception as e:
results.append(f"ERROR: {e}")
return results
funcs = [str, int, lambda x: x / 0]
print(run_all(funcs, "42"))
# ["42", 42, "ERROR: division by zero"]