Генерация исключений при помощи raise
Основы использования raise
В Python оператор raise позволяет программисту явно вызвать исключение. Это бывает необходимо, когда код обнаруживает недопустимую ситуацию, которую не может обработать самостоятельно. Основная цель raise - сигнализировать об ошибке и передать управление соответствующему обработчику.
Основной способ вызова исключения - указать класс исключения и, при необходимости, сообщение:
raise ValueError("Недопустимое значение")вызов ошибки python (вызов ошибки (raise) в python)
После выполнения этой строки программа немедленно прерывает нормальный поток и ищет подходящий блок except. Если блок не найден, выполнение завершается с сообщением об ошибке.
Цель: явно сигнализировать об ошибке, которую невозможно обработать локально.
Как вызвать исключение только по классу, без сообщения?
Можно передать только класс исключения. При этом будет создан экземпляр без аргументов:
raise ValueErrorТакой способ удобен, когда сообщение не требуется или исключение будет обработано по типу.
Проблема: при отладке сложнее понять причину ошибки, если сообщение отсутствует. Решение - добавлять хотя бы краткое описание.
Как повторно возбудить перехваченное исключение?
Внутри блока except можно использовать raise без аргументов - это перебросит текущее исключение дальше по стеку:
try:
x = int("abc")
except ValueError:
print("Логирование...")
raiseЭто позволяет выполнить дополнительную обработку (например, запись в лог), но не скрывать ошибку от вышестоящего кода.
Типичная ошибка: использование raise без аргумента вне блока except вызывает RuntimeError. Решение - всегда проверять контекст.
Как сохранить цепочку исключений (причина)?
Конструкция raise ... from связывает новое исключение с исходным:
try:
result = risky_operation()
except ValueError as cause:
raise RuntimeError("Операция не удалась") from causeВ итоге в traceback будет показана вся цепочка: RuntimeError, вызванное ValueError. Это упрощает отладку.
Проблема: если указать from None, цепочка подавляется. Это может скрыть важную информацию. Решение - использовать from только когда это необходимо.
Как создать и использовать собственное исключение?
Пользовательские исключения наследуются от Exception (или его подклассов):
class MyCustomError(Exception):
pass
raise MyCustomError("Ошибка в бизнес-логике")Это помогает структурировать типы ошибок в проекте и обрабатывать их выборочно.
Ошибка: наследование от BaseException (вместо Exception) может перехватывать системные события (например, SystemExit). Решение - всегда наследовать от Exception.
Как передать уже созданный экземпляр исключения?
Можно вызвать raise с готовым объектом:
err = ValueError("Неверный ввод")
raise errЭто удобно, когда исключение формируется заранее, например, в функции-валидаторе.
Проблема: изменение экземпляра после вызова raise не влияет на исключение. Решение - создавать новый экземпляр при каждом выбрасывании.
def divide(a, b):
if b == 0:
raise ZeroDivisionError("Деление на ноль")
return a / b
try:
result = divide(10, 0)
except ZeroDivisionError as e:
print(f"Ошибка: {e}")Ошибка: Деление на ноль
class ValidationError(Exception):
def __init__(self, field, message):
self.field = field
self.message = message
super().__init__(f"{field}: {message}")
def validate_age(age):
if age < 0:
raise ValidationError("age", "Возраст не может быть отрицательным")
return age
try:
validate_age(-5)
except ValidationError as ve:
print(f"Ошибка в поле {ve.field}: {ve.message}")Ошибка в поле age: Возраст не может быть отрицательным
def fetch_data(url):
try:
# имитация запроса
if not url.startswith("https"):
raise ConnectionError("Небезопасное соединение")
except ConnectionError as orig:
raise RuntimeError("Не удалось получить данные") from orig
try:
fetch_data("http://example.com")
except RuntimeError as e:
print(f"Конечная ошибка: {e}")
print(f"Причина: {e.__cause__}")Конечная ошибка: Не удалось получить данные Причина: Небезопасное соединение
def safe_division(a, b):
try:
return a / b
except ZeroDivisionError:
print("Произошло деление на ноль, логируем...")
raise # переброс
try:
safe_division(5, 0)
except ZeroDivisionError:
print("Ошибка обработана выше")Произошло деление на ноль, логируем... Ошибка обработана выше
class Resource:
def __enter__(self):
print("Ресурс открыт")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("Ресурс закрыт")
if exc_type is ValueError:
print("Подавляем ValueError")
return True # подавление исключения
return False
with Resource():
raise ValueError("Ошибка в блоке with")Ресурс открыт Ресурс закрыт Подавляем ValueError