__cause__ или функции raise ... from .... Это позволяет отслеживать всю цепочку событий, приведших к ошибке.
try:
# ... что-то, что может вызвать ValueError
raise ValueError("Ошибка преобразования типа")
except ValueError as e:
try:
# ... обработка ValueError и возможно возникновение IOError
raise IOError("Ошибка ввода/вывода")
except IOError as e2:
raise RuntimeError("Критическая ошибка") from e2 # Связываем с IOError
except IOError as e3:
raise RuntimeError("Критическая ошибка (IOError)") from e3 # Связываем с IOError
RuntimeError, атрибут __cause__ содержит оригинальный IOError (или ValueError, в зависимости от того, где была ошибка), позволяя провести детальный анализ причин ошибки. Использование raise ... from None отключает цепочки исключений.
Для создания исключений с несколькими уровнями вложенности в Python, можно использовать механизм, когда одно исключение вызывает другое, сохраняя информацию о причине ошибки на каждом уровне. Это позволяет получить более подробную картину произошедшего и облегчает отладку сложных ситуаций.
Основной подход заключается в использовании аргумента from в инструкции raise. С помощью raise ExceptionA from ExceptionB мы указываем, что ExceptionA вызвано (или является прямым следствием) ExceptionB.
Пример:
class DatabaseError(Exception):
"""Общий класс для ошибок базы данных."""
pass
class ConnectionError(DatabaseError):
"""Ошибка подключения к базе данных."""
pass
class QueryError(DatabaseError):
"""Ошибка выполнения запроса к базе данных."""
pass
def connect_to_database(host, port):
"""Попытка подключения к базе данных."""
try:
# Имитация неудачного подключения
raise OSError("Ошибка сети: невозможно подключиться к хосту")
except OSError as e:
raise ConnectionError("Не удалось установить соединение с базой данных") from e
def execute_query(connection, query):
"""Попытка выполнить запрос."""
try:
# Имитация ошибки в запросе
raise ValueError("Некорректный SQL запрос")
except ValueError as e:
raise QueryError("Ошибка при выполнении запроса") from e
def process_data(host, port, query):
"""Основная функция обработки данных."""
try:
connection = connect_to_database(host, port)
execute_query(connection, query)
except DatabaseError as e:
print(f"Произошла ошибка базы данных: {e}")
if e.__cause__:
print(f" Вызвано исключением: {e.__cause__}") #Выводит исходную ошибку
except Exception as e:
print(f"Непредвиденная ошибка: {e}")
# Пример использования
process_data("localhost", 5432, "SELECT * FROM invalid_table")
В данном примере:
DatabaseError -> ConnectionError, QueryError.connect_to_database ловит OSError и генерирует ConnectionError, указывая, что причиной была сетевая ошибка.execute_query ловит ValueError и генерирует QueryError, указывая, что причиной была ошибка в запросе.process_data обрабатывает исключение DatabaseError и, если у него есть причина (__cause__), выводит информацию о ней.Преимущества такого подхода:
DatabaseError и повторить операцию, если причиной была временная сетевая проблема (ConnectionError).Важно: При создании вложенных исключений следите за тем, чтобы каждое новое исключение предоставляло дополнительную информацию, а не просто повторяло предыдущее. Аргумент from следует использовать только тогда, когда новое исключение действительно вызвано предыдущим.
Альтернативные подходы (встречаются реже):
В заключение, создание исключений с несколькими уровнями вложенности с помощью аргумента from является мощным инструментом для обработки сложных ошибок в Python. Он обеспечивает сохранение цепочки причин, упрощает отладку и делает сообщения об ошибках более информативными.