Исключения в Python: классы ошибок и их наследование
Основы классов исключений в Python
В Python все исключения представлены классами, образующими иерархию. Корнем является BaseException. От него наследуются SystemExit, KeyboardInterrupt, GeneratorExit и Exception. Большинство исключений, с которыми работают разработчики, наследуются от Exception. Правильная организация перехвата исключений основана на порядке следования блоков except: от более специфичных к более общим.
try:
number = int(input("Введите число: "))
result = 10 / number
except ValueError:
print("Введено не число")
except ZeroDivisionError:
print("Деление на ноль")
except Exception as e:
print(f"Неизвестная ошибка: {e}")Python создать ошибку (создание исключений в python)
Цель:
обеспечить корректную обработку разных типов ошибок, не перекрывая специфичные обработчики общими.Как создать собственное исключение?
Для создания пользовательского исключения достаточно определить класс, наследующий от Exception (или его подкласса). Это позволяет идентифицировать специфические ошибки в коде.
class InsufficientFundsError(Exception):
pass
def withdraw(balance, amount):
if amount > balance:
raise InsufficientFundsError("Недостаточно средств")
return balance - amount
try:
new_balance = withdraw(100, 150)
except InsufficientFundsError as e:
print(e)класс ошибок python (классы исключений в python)
Типичные проблемы:
- Забыли наследовать от Exception. Если класс наследует от object, он не будет исключением и не будет перехвачен блоком except.
- Использование слишком общего исключения в качестве базового для всех ошибок приложения - затрудняет тонкую обработку.
Как добавить дополнительную информацию в исключение?
Через переопределение __init__ и __str__ можно передавать структурированные данные.
class HTTPRequestError(Exception):
def __init__(self, status_code, message):
self.status_code = status_code
self.message = message
super().__init__(f"HTTP {status_code}: {message}")
try:
raise HTTPRequestError(404, "Not Found")
except HTTPRequestError as e:
print(f"Код: {e.status_code}, Сообщение: {e.message}")Python except (обработка исключений в python с помощью try-except)
Проблема:
забыть вызвать super().__init__() - тогда при преобразовании в строку (например, при логировании) может отображаться пустое сообщение.Как сохранить контекст исходной ошибки (цепочка исключений)?
Конструкция raise ... from позволяет связать новое исключение с исходным, сохраняя информацию о первопричине.
def parse_int(s):
try:
return int(s)
except ValueError as e:
raise ValueError("Невозможно преобразовать строку в число") from e
try:
parse_int("abc")
except ValueError as e:
print("Исключение:", e)
print("Причина:", e.__cause__)
Типичная ошибка:
использование from None подавляет цепочку, теряя контекст. Это следует делать осознанно, когда исходная ошибка не должна быть видна.Как обрабатывать несколько исключений одновременно (ExceptionGroup)?
Начиная с Python 3.11, можно использовать ExceptionGroup для группировки исключений и перехвата их с помощью except*.
try:
raise ExceptionGroup("Ошибки валидации",
[ValueError("поле name пустое"),
TypeError("поле age не число")])
except* ValueError as eg:
print("Перехвачены ValueError:", eg.exceptions)
except* TypeError as eg:
print("Перехвачены TypeError:", eg.exceptions)
Ограничение:
ExceptionGroup доступен только в Python 3.11+. Для более старых версий потребуется собственная реализация группового хранения исключений.Расширенные примеры работы с классами исключений
Пример 1: Иерархия исключений для библиотеки
class BaseLibraryError(Exception):
"""Базовое исключение библиотеки"""
pass
class NetworkError(BaseLibraryError):
"""Ошибка сети"""
def __init__(self, url, status_code, message=""):
self.url = url
self.status_code = status_code
super().__init__(f"Network error {status_code} at {url}: {message}")
class AuthError(BaseLibraryError):
"""Ошибка аутентификации"""
def __init__(self, user, message="Authentication failed"):
self.user = user
super().__init__(f"Auth error for {user}: {message}")
try:
raise NetworkError("https://api.example.com", 401, "Unauthorized")
except BaseLibraryError as e:
print(type(e).__name__, ":", e)
NetworkError : Network error 401 at https://api.example.com: Unauthorized
Пример 2: Исключение со словарём ошибок и методом to_dict
class ValidationError(Exception):
def __init__(self, errors: dict):
self.errors = errors
msg = "; ".join(f"{field}: {err}" for field, err in errors.items())
super().__init__(msg)
def to_dict(self):
return self.errors
try:
raise ValidationError({"email": "invalid format", "age": "must be integer"})
except ValidationError as e:
print("Строковое представление:", str(e))
print("Словарь ошибок:", e.to_dict())
Строковое представление: email: invalid format; age: must be integer
Словарь ошибок: {'email': 'invalid format', 'age': 'must be integer'}
Пример 3: Цепочка исключений с __cause__ и трассировка
import traceback
class DatabaseError(Exception):
def __init__(self, query, original_exception):
self.query = query
self.original = original_exception
super().__init__(f"Database error on query '{query}': {original_exception}")
def execute_query(query):
try:
raise ConnectionError("Cannot connect to database")
except ConnectionError as ce:
raise DatabaseError(query, ce) from ce
try:
execute_query("SELECT * FROM users")
except DatabaseError as de:
print("Трассировка:", traceback.format_exception_only(type(de), de))
print("Цепочка: __cause__:", de.__cause__)
Трассировка: ['DatabaseError: Database error on query \'SELECT * FROM users\': Cannot connect to database\n'] Цепочка: __cause__: Cannot connect to database