Привет! Ты уже знаешь, что исключения возникают, когда Python сталкивается с ошибкой: деление на ноль, обращение к несуществующему ключу в словаре, открытие отсутствующего файла. Но что, если ты сам хочешь сказать программе: «Это неправильно, обработай это как ошибку!»? Для этого служит оператор raise.
В этой статье мы разберём:
- Зачем вообще вручную вызывать исключения
- Синтаксис
raiseи как добавить сообщение об ошибке - Какой тип исключения выбрать (встроенные и свои)
- Как создавать собственные классы исключений
- Проброс исключений с сохранением стека вызовов
- Связывание исключений с помощью
raise ... from
- Что нужно знать перед началом
- Основная часть
- 1. Зачем вручную вызывать исключения?
- Синтаксис raise
- Простейший способ — вызвать встроенное исключение
- С сообщением об ошибке (рекомендуется)
- Можно передать несколько аргументов
- Выбор правильного типа исключения
- Создание своих исключений
- Проброс исключений (re-raise)
- Связывание исключений: raise … from
- Задачи для закрепления
- Нюансы и подводные камни
- raise перезаписывает контекст
- Не вызывай Exception в лоб
- Игнорирование ошибок
- Частые ошибки и как их избежать
- Ошибка 1: Вызов слишком общего исключения
- Ошибка 2: Попытка повторного вызова без сохранения контекста
- Ошибка 3: Использование устаревшего синтаксиса
- Шпаргалка
- Заключение
- КВИЗ
- Что дальше?
Что нужно знать перед началом
Для этого урока тебе понадобится:
- Установленный Python
- Базовое понимание исключений из предыдущих статей
- Знание конструкции
try-except
Совет: Вручную вызывать исключения стоит тогда, когда ты хочешь явно указать на ошибку в своей логике. Это делает код надёжнее и понятнее.
Основная часть
1. Зачем вручную вызывать исключения?
Иногда программе нужно остановить выполнение, потому что дальше работать нельзя: пользователь ввёл отрицательный возраст, файл имеет неправильный формат, аргумент функции не прошёл проверку. Вместо того чтобы тихо вернуть непонятное значение или просто сломаться, лучше явно сообщить об ошибке с помощью raise.
«Обычно код, который вызывает функцию, а не сама функция, знает, как обработать исключение. Поэтому вы часто увидите оператор
raiseвнутри функции, а операторыtryиexcept— в коде, вызывающем функцию.»
Совет: Программист, который будет использовать твой код, будет благодарен за чёткие и понятные сообщения об ошибках.
raise— это твой способ сказать «Эй, здесь что-то пошло не так!».
Синтаксис raise
Оператор raise состоит из ключевого слова raise и объекта исключения (или класса исключения) .
Простейший способ — вызвать встроенное исключение
# Просто поднимаем исключение без дополнительной информации
raise ValueErrorРезультат:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueErrorС сообщением об ошибке (рекомендуется)
raise ValueError('А вот это уже полезное сообщение!')Результат:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: А вот это уже полезное сообщение!Совет: Всегда добавляй сообщение к исключению. Это помогает понять, что именно пошло не так.
Синтаксис: raise + Exception('ваше сообщение')
Можно передать несколько аргументов
Иногда полезно передать не только сообщение, но и другие данные, чтобы обработчик мог принять правильное решение.
raise ValueError('Ошибка', 'дополнительный_параметр', 123)В обработчике: эти данные будут доступны через err.args.
Выбор правильного типа исключения
Python предлагает множество встроенных исключений. Важно выбирать самое подходящее .
Совет: Никогда не вызывай
raise Exception('...')без особой причины. Это слишком общий тип, и его перехват может скрыть другие, более серьёзные ошибки .
Пример правильного выбора:
Допустим, мы пишем функцию для установки возраста.
def set_age(age):
if not isinstance(age, int):
# Правильный тип? Нет! TypeError
raise TypeError(f'Возраст должен быть числом, а не {type(age).__name__}')
if age < 0:
# Тип правильный (int), но значение недопустимое. ValueError!
raise ValueError('Возраст не может быть отрицательным')
print(f'Возраст установлен: {age}')Пример использования:
try:
set_age(-5)
except TypeError as e:
print(f'Ошибка типа: {e}')
except ValueError as e:
print(f'Ошибка значения: {e}')Результат:
Ошибка значения: Возраст не может быть отрицательнымСоздание своих исключений
Бывает, что встроенные исключения слишком общие для твоей задачи. Тогда можно создать свой класс исключения, унаследовав его от Exception.
class ValidationError(Exception):
"""Исключение для ошибок валидации данных."""
def __init__(self, message, field=None):
super().__init__(message)
self.field = field
def validate_user(name, age):
if not name:
raise ValidationError('Имя пользователя не может быть пустым', field='name')
if age < 0:
raise ValidationError('Возраст не может быть отрицательным', field='age')
return True
# Использование
try:
validate_user('', -5)
except ValidationError as e:
print(f'Ошибка в поле "{e.field}": {e}')Совет: Называй свои исключения с суффиксом
ErrorилиException, чтобы было понятно, что это класс ошибки.
Проброс исключений (re-raise)
Иногда ты хочешь перехватить исключение, записать его в лог или сделать что-то ещё, а потом позволить ему всплыть выше, чтобы его обработал другой код. Для этого используется raise без аргументов — он пробрасывает текущее исключение дальше.
import logging
logger = logging.getLogger(__name__)
def process_data(data):
try:
# ... какой-то опасный код ...
return data[0] / data[1]
except ZeroDivisionError as e:
logger.error('Попытка деления на ноль!')
raise # Пробрасываем исключение дальше
# Вызывающий код
try:
result = process_data([1, 0])
except ZeroDivisionError:
print('Кто-то пытался поделить на ноль!')Совет: Используй raise без аргументов, чтобы сохранить полный стек вызовов и упростить отладку.
Связывание исключений: raise … from
В Python 3 появилась возможность связывать исключения. Это полезно, когда ты хочешь заменить одно исключение другим, но при этом сохранить информацию о первопричине.
try:
# Какой-то код, который может вызвать ошибку
int('abc')
except ValueError as e:
# Мы хотим сказать: "Это произошло из-за ошибки в данных"
raise RuntimeError('Не удалось преобразовать данные') from eРезультат:
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ValueError: invalid literal for int() with base 10: 'abc'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
RuntimeError: Не удалось преобразовать данныеСовет: Это помогает разработчику понять цепочку ошибок, особенно когда ты работаешь с разными уровнями абстракции в коде.
Задачи для закрепления
Задача 1. Напиши функцию divide(a, b), которая вызывает ZeroDivisionError, если b == 0.
Задача 2. Напиши функцию set_age(age), которая вызывает ValueError при отрицательном возрасте.
Задача 3. Создай свой класс исключения NegativeNumberError и используй его для валидации положительных чисел.
Задача 4. Перехвати TypeError, а затем пробрось его дальше с помощью raise.
Задача 5. Изучи разницу между raise и raise ... from на примере.
Ответы:
Задача 1.
def divide(a, b):
if b == 0:
raise ZeroDivisionError('На ноль делить нельзя!')
return a / bЗадача 2.
def set_age(age):
if age < 0:
raise ValueError('Возраст не может быть отрицательным')
return ageЗадача 3.
class NegativeNumberError(Exception):
pass
def check_positive(num):
if num < 0:
raise NegativeNumberError(f'Число {num} отрицательное!')
return TrueЗадача 4.
def func():
try:
'abc' + 5
except TypeError:
print('Логируем ошибку...')
raise # Пробрасываем TypeError дальшеЗадача 5.
raise — просто выбрасывает новое исключение. raise ... from — связывает новое исключение с предыдущим, показывая, что они связаны по смыслу.
Нюансы и подводные камни
raise перезаписывает контекст
Если ты просто используешь raise с новым исключением, ты потеряешь информацию о том, что произошло ранее. from помогает этого избежать .
Не вызывай Exception в лоб
Использование raise Exception('...') затрудняет отладку, так как это слишком общее исключение. Всегда старайся использовать что-то более конкретное .
Игнорирование ошибок
Иногда программисты пишут пустой except: pass. Это плохая практика, потому что ты можешь случайно скрыть важную ошибку.
Частые ошибки и как их избежать
Ошибка 1: Вызов слишком общего исключения
# Плохо — лень, но скрывает детали
raise Exception('Ошибка!')
# Хорошо — конкретное исключение
raise ValueError('Передано неверное значение')Ошибка 2: Попытка повторного вызова без сохранения контекста
# Плохо — потеряли стек вызовов
try:
1 / 0
except ZeroDivisionError:
raise ValueError('Деление на ноль')
# Теряем оригинальную причину!
# Хорошо — связываем исключения
try:
1 / 0
except ZeroDivisionError as e:
raise ValueError('Деление на ноль') from eОшибка 3: Использование устаревшего синтаксиса
Не используй устаревший синтаксис Python 2: raise ValueError, 'message' .
Шпаргалка
| Что нужно | Как пишется |
|---|---|
| Поднять исключение | raise ValueError |
| Поднять с сообщением | raise ValueError('Текст ошибки') |
| Пробросить текущее исключение | raise |
| Связать исключение | raise NewError('...') from old_error |
| Создать свой класс ошибки | class MyError(Exception): pass |
Заключение
Сегодня мы научились:
- Вручную вызывать исключения с помощью
raise - Подбирать подходящий встроенный тип исключения
- Создавать свои классы исключений
- Пробрасывать исключения дальше с сохранением стека
- Связывать исключения с помощью
raise ... from






