← Назад к вопросам
Зачем нужно перехватывать ошибку в Python?
1.6 Junior🔥 251 комментариев
#Python Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Зачем нужно перехватывать ошибку в Python
Перехват ошибок (exception handling) в Python нужен для управления непредвиденными ситуациями и обеспечения стабильности приложения. Без перехвата — приложение крахнется.
Проблема без перехвата
# Без перехвата ошибок
def divide(a, b):
return a / b # Если b = 0, программа упадёт!
print(divide(10, 2)) # 5.0 — OK
print(divide(10, 0)) # ZeroDivisionError — приложение упало!
print("This line never executes") # Не выполнится
# Вывод:
# 5.0
# Traceback (most recent call last):
# File "script.py", line 8, in <module>
# print(divide(10, 0))
# File "script.py", line 3, in divide
# return a / b
# ZeroDivisionError: division by zero
Приложение упало! Пользователь получит 500 ошибку.
Решение: try-except
def divide(a, b):
try:
result = a / b # Рискованный код
except ZeroDivisionError:
print("Error: Cannot divide by zero")
result = None
return result
print(divide(10, 2)) # 5.0
print(divide(10, 0)) # Error: Cannot divide by zero
print("Program continues...") # Выполняется!
# Вывод:
# 5.0
# Error: Cannot divide by zero
# Program continues...
Основные типы ошибок
# SyntaxError — ошибка синтаксиса (при парсинге)
if True # Missing colon
# IndentationError — неправильный отступ
if True:
print('hello') # Неправильный отступ
# NameError — переменная не определена
print(undefined_variable)
# TypeError — неправильный тип
print("hello" + 5) # Нельзя складывать string и int
# ValueError — неправильное значение
int("not a number")
# ZeroDivisionError — деление на ноль
print(10 / 0)
# IndexError — индекс вне диапазона
list_var = [1, 2, 3]
print(list_var[10])
# KeyError — ключ не в словаре
dict_var = {'a': 1}
print(dict_var['b'])
# FileNotFoundError — файл не найден
with open('/nonexistent/file.txt') as f:
pass
# AttributeError — атрибут не существует
class MyClass:
pass
obj = MyClass()
print(obj.undefined_attr)
try-except-else-finally
def read_file(filename):
try:
# Код, который может вызвать ошибку
with open(filename) as f:
content = f.read()
except FileNotFoundError:
# Обработка конкретной ошибки
print(f"File {filename} not found")
content = None
except IOError as e:
# Захват ошибки в переменную
print(f"IO Error: {e}")
content = None
except Exception as e:
# Ловля всех остальных ошибок (bad practice!)
print(f"Unexpected error: {e}")
content = None
else:
# Выполняется если НЕ было ошибки
print(f"Successfully read {len(content)} characters")
finally:
# Выполняется ВСЕГДА (cleanup)
print("File operation completed")
return content
result = read_file('data.txt')
Практические примеры
Пример 1: API запрос с перехватом
import requests
from requests.exceptions import RequestException, Timeout, ConnectionError
def fetch_user_data(user_id):
try:
response = requests.get(
f'https://api.example.com/users/{user_id}',
timeout=5
)
response.raise_for_status() # Throws HTTPError for bad status
return response.json()
except Timeout:
print(f"Request timeout for user {user_id}")
return None
except ConnectionError:
print("Network connection failed")
return None
except requests.HTTPError as e:
if e.response.status_code == 404:
print(f"User {user_id} not found")
else:
print(f"HTTP Error: {e}")
return None
except Exception as e:
print(f"Unexpected error: {e}")
return None
user = fetch_user_data(123)
if user:
print(f"User name: {user['name']}")
else:
print("Could not fetch user")
Пример 2: Валидация и логирование
import logging
logger = logging.getLogger(__name__)
def process_user_input(user_input):
try:
# Конвертируем строку в число
age = int(user_input)
if age < 0 or age > 150:
raise ValueError(f"Invalid age: {age}")
return age
except ValueError as e:
# Логируем ошибку
logger.error(f"Invalid input: {user_input}. Error: {e}")
# Возвращаем default значение
return None
except Exception as e:
logger.exception(f"Unexpected error processing input: {user_input}")
return None
age = process_user_input("25")
if age is not None:
print(f"User age: {age}")
Пример 3: Context Manager (with statement)
В Python можно автоматизировать cleanup с with:
# Без перехвата (может упасть)
def read_file_bad(filename):
f = open(filename)
content = f.read()
f.close() # Может не выполниться если произойдёт ошибка!
return content
# С перехватом
def read_file_good(filename):
try:
with open(filename) as f: # Автоматический close!
return f.read()
except FileNotFoundError:
return ""
# Или создать свой context manager
class Database:
def __init__(self, connection_string):
self.conn = None
self.connection_string = connection_string
def __enter__(self):
try:
self.conn = connect(self.connection_string)
return self.conn
except ConnectionError as e:
print(f"Failed to connect: {e}")
raise
def __exit__(self, exc_type, exc_val, exc_tb):
# Выполняется ВСЕГДА
if self.conn:
self.conn.close()
# Можно перехватить ошибки
if exc_type is not None:
print(f"Error in database context: {exc_val}")
return False # Пробросить исключение дальше
# Использование
try:
with Database('postgresql://localhost/mydb') as db:
result = db.query('SELECT * FROM users')
except ConnectionError:
print("Could not connect to database")
Пример 4: Retry logic
import time
from functools import wraps
def retry(max_attempts=3, delay=1):
"""
Декоратор для повторного выполнения функции при ошибке
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt < max_attempts - 1:
print(f"Attempt {attempt + 1} failed: {e}. Retrying...")
time.sleep(delay)
else:
print(f"All {max_attempts} attempts failed")
raise
return wrapper
return decorator
@retry(max_attempts=3, delay=2)
def unstable_api_call():
# Функция иногда падает
import random
if random.random() < 0.7:
raise ConnectionError("API unavailable")
return "Success!"
result = unstable_api_call() # Повторит 3 раза если нужно
print(result)
Пример 5: Цепочка обработки ошибок
class CustomError(Exception):
"""Кастомная ошибка"""
pass
def process_data(data):
try:
if not data:
raise ValueError("Data is empty")
if len(data) < 10:
raise CustomError("Data too short")
# Обработка
result = data.upper()
return result
except ValueError as e:
print(f"Validation error: {e}")
return None
except CustomError as e:
print(f"Custom error: {e}")
return data # Return original
except Exception as e:
print(f"Unexpected error: {e}")
raise # Пробросить дальше
print(process_data("")) # ValueError
print(process_data("short")) # CustomError
print(process_data("long enough data")) # Success
Лучшие практики
✅ Делай так:
try:
risky_operation()
except SpecificError as e:
# Обработай конкретную ошибку
handle_error(e)
finally:
cleanup() # Cleanup всегда
❌ Не делай так:
try:
risky_operation()
except: # Ловит ВСЕ ошибки (включая KeyboardInterrupt!)
pass # Игнорируем ошибку
try:
risky_operation()
except Exception: # Очень общее
pass # Скрываем проблему
Иерархия ошибок
BaseException
├── SystemExit
├── KeyboardInterrupt (Ctrl+C)
├── GeneratorExit
└── Exception (Почти всё)
├── ValueError
├── TypeError
├── KeyError
├── IndexError
├── FileNotFoundError
├── requests.RequestException
└── ... 100+ других
Вывод
Перехват ошибок нужен для:
- Стабильности — приложение не падает
- Graceful degradation — fallback поведение
- Логирования — понимаем что пошло не так
- Очистки ресурсов — finally/with
- Retry logic — повторная попытка
Без перехвата ошибок приложение будет нестабильным и сложным в отладке. Всегда используй try-except для обработки предсказуемых ошибок.