raise: бросаем исключение сами
До сих пор ошибки бросал Python. Теперь научимся бросать сами: raise ValueError с понятным текстом, когда данные нарушают правила программы.
Редакция Питоники
Все аварии первых шести уроков бросал сам Python: не смог разобрать строку — ValueError, делить на ноль нельзя — ZeroDivisionError. Но у программы есть и собственные правила, которые Python не знает: возраст не бывает отрицательным, логин не бывает пустым, оценка не бывает больше ста. Когда данные нарушают такое правило, программист бросает исключение сам — командой raise.
Нарушенное правило — это ошибка
Возраст -5 — не крах операции: Python спокойно хранит отрицательные числа. Крах логический: данные не соответствуют смыслу задачи. Молча продолжать с такими данными опасно — отчёт посчитает «минус пять лет». Правильная реакция — громкая: остановить обработку и объявить, что правило нарушено.
age = -5
try:
if age < 0:
raise ValueError('возраст не может быть отрицательным')
print('Принят возраст:', age)
except ValueError as e:
print('Отклонено:', e)
Отклонено: возраст не может быть отрицательным
Условие нашло нарушение — raise бросил ValueError с нашим текстом. Дальше всё знакомое: исключение полетело в ближайший except, обработчик поймал его через as e и напечатал сообщение. С точки зрения except нет никакой разницы, кто бросил ошибку — Python или наша строка кода.Raise — это штатный способ сказать «дальше с такими данными не работаю».
age = 30
try:
if age < 0:
raise ValueError('возраст не может быть отрицательным')
print('Принят возраст:', age)
except ValueError as e:
print('Отклонено:', e)
Принят возраст: 30
Условие не сработало, raise не выполнился — данные прошли насквозь и попали в печать приёма. Проверка не наказывает хорошие данные: она спит, пока правило не нарушено.
raise без ловли: честное падение
Что будет, если бросить исключение там, где рядом нет try? То же, что с любой непойманной ошибкой — traceback и остановка программы:
age = -5
if age < 0:
raise ValueError('возраст не может быть отрицательным')
print('Продолжаем')
Traceback (most recent call last):
File "app.py", line 3, in <module>
raise ValueError('возраст не может быть отрицательным')
ValueError: возраст не может быть отрицательнымraise превращает нарушенное правило данных в настоящую ошибку Python — с типом, понятным текстом и traceback. Traceback здесь указывает на нашу строку с raise, а в последней строке — тип и текст, которые мы выбрали. Для вызывающего кода это самое честное сообщение: правило нарушено, вот какое. Убрать проверку или заменить её молчаливой печатью — значит позволить плохим данным течь дальше по программе.
Текст ошибки — сообщение для людей
В скобках после типа пишется сообщение. Без него ошибка тоже бросится, но скажет только тип:
try:
raise ValueError
except ValueError as e:
print('Тип:', type(e).__name__)
print('Сообщение:', repr(str(e)))
Тип: ValueError Сообщение: ''
Сообщение оказалось пустой строкой: рассказать, что именно не так, некому. Кто поймает такую ошибку — увидит тип, но не причину. Поэтому правило урока: raise всегда с текстом. Текст — это не украшение, а сама суть: он объясняет нарушенное правило словами.
login = 'a' * 25
try:
if len(login) == 0:
raise ValueError('логин пуст')
if len(login) > 20:
raise ValueError('логин длиннее 20 символов')
print('Логин принят:', login)
except ValueError as e:
print('Отклонено:', e)
Отклонено: логин длиннее 20 символов
Две проверки, два raise с разными текстами. Сработало то нарушение, которое условие нашло первым: логин непустой, но длинный — вторая проверка остановила обработку. Каждый raise — это одно правило и одна формулировка: не «данные плохие вообще», а конкретно «логин длиннее 20 символов».
Проверка данных в цикле: брак мимо отчёта
Соберём рабочую схему: сумма оценок, где каждая выше сотни — нарушение правила. Нарушившие не роняют отчёт, а попадают в протокол:
scores = [90, 105, 70]
total = 0
for s in scores:
try:
if s > 100:
raise ValueError(f'оценка {s} больше 100')
total += s
except ValueError as e:
print('Пропущен:', e)
print('Сумма корректных:', total)
Пропущен: оценка 105 больше 100 Сумма корректных: 160
Оценка 105 нарушила правило — raise остановил её обработку и бросил ошибку с её номером в тексте. Обработчик поймал, напечатал протокол, и цикл перешёл к семидесятке, которая вошла в сумму. f-строка в сообщении здесь не украшение: без неё протокол сказал бы «оценка больше 100» — а какую именно, осталось бы тайной.
Какой тип бросать
Бросать стоит стандартные типы — по смыслу нарушения. ValueError, если не подошло значение: отрицательный возраст, пустой логин, оценка больше ста. TypeError, если не подошёл сам тип данных: вместо числа пришла строка. Точный тип помогает обработчику: цепочка except из четвёртого урока различит «плохое значение» и «плохой тип».
| Нарушение | Тип | Пример |
|---|---|---|
| Значение вне допустимого диапазона | ValueError | raise ValueError('возраст отрицательный') |
| Не тот тип данных | TypeError | raise TypeError('ожидали число') |
| Ключа нет, а он обязателен | KeyError | raise KeyError('логин') |
| Операция преждевременна | RuntimeError | raise RuntimeError('сначала откройте файл') |
А вот бросать строку нельзя — даже если очень хочется сообщить текст. Исключение — это объект особого рода, и Python проверяет это строго:
raise 'что-то пошло не так'
Traceback (most recent call last):
File "app.py", line 1, in <module>
raise 'что-то пошло не так'
TypeError: exceptions must derive from BaseExceptionraise в общей схеме урока
- raise ValueError('текст') бросает исключение с сообщением — выполнение останавливается на этой строке;
- ближайший except ловит ваш raise так же, как ошибку Python: тип, сообщение через as e;
- без обработчика ваш raise падает с traceback — и это правильно: вызывающий код узнаёт о нарушении;
- текст пишите всегда и конкретно: правило, которое нарушено, и значение-нарушитель;
- тип подбирайте по смыслу: ValueError — значение, TypeError — тип данных.
Мы научились сообщать об ошибках так же, как это делает сам Python: с типом, текстом и traceback. Проверка правил через raise работает в паре с обработкой: try держит рискованную проверку, except реагирует, finally подчищает. Когда стандартных типов не хватит, у программы появятся собственные классы исключений с именами, которые читаются в логах, — им посвящён отдельный урок дальше по разделу.
Сначала предскажи ответ в голове — это главный навык программиста.
value = 0
try:
if value == 0:
raise ValueError('ноль не принимаем')
print('принято')
except ValueError as e:
print('отказ:', e)
print('конец')
value = 7
try:
if value == 0:
raise ValueError('ноль не принимаем')
print('принято:', value)
except ValueError as e:
print('отказ:', e)
for s in [50, 120]:
try:
if s > 100:
raise ValueError(f'{s} выше лимита')
print(s, 'в норме')
except ValueError as e:
print('пропуск:', e)
1. Что делает команда raise ValueError('текст')?
2. Выполнятся ли строки кода, написанные после сработавшего raise?
3. Чем для except отличается ваш raise от ошибки, брошенной Python?
4. Что напечатает str(e) после raise ValueError?
5. Случился raise 'строка'. Что произойдёт?
6. Почему печать предупреждения — плохая замена raise?
7. Какой тип правильнее бросить, если вместо числа пришла строка?
8. Зачем в сообщении raise f'оценка {s} больше 100' используют f-строку?
Дано age = -3. Внутри try проверь правило: если возраст отрицательный, брось ValueError с текстом «возраст не может быть отрицательным». При успехе печатай «Принят возраст: N», при нарушении — «Отклонено: <текст>».
Как бросить исключение в python вручную?
Командой raise с типом и сообщением: raise ValueError('описание нарушения'). Выполнение остановится на этой строке, а исключение уйдёт в ближайший except или упадёт с traceback, если ловить некому.
Урок 6: else и finally — два хвоста tryКогда стоит бросать исключение самому?
Когда данные нарушают правила программы: отрицательный возраст, пустой логин, оценка больше ста. Молча продолжать опасно — отчёты посчитают неверные значения. raise громко останавливает обработку и объясняет, какое правило нарушено.
Какой тип исключения выбирать для raise?
Стандартный, по смыслу нарушения: ValueError — не подошло значение, TypeError — не подошёл тип данных, KeyError — обязательного не хватает. Текст пишите всегда: он объясняет правило словами, без него сообщение будет пустым.
Можно ли написать raise 'строка с текстом'?
Нет: бросать можно только объекты классов исключений. Python ответит TypeError: exceptions must derive from BaseException. Текст передаётся в скобках: raise ValueError('строка с текстом').
Как обработать собственное исключение?
Как обычное: обернуть проверку в try и написать except с тем же типом. Сообщение попадёт в переменную через as e — его можно напечатать в протокол. Разница между ошибкой Python и вашим raise для обработчика отсутствует.
Понравился урок? Сошлитесь на него
«raise превращает нарушенное правило данных в настоящую ошибку Python — с типом, понятным текстом и traceback.»
Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.
Что дальше
Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.
Что читать дальше
Исключения · Урок 4
Несколько except: разные ошибки — разные реакции
Цепочка except-блоков: ValueError — один план, ZeroDivisionError — другой. Порядок обработчиков, правило одного обработчика и кортеж типов.
Исключения · Урок 5
as e: объект ошибки в переменной
Обработчик узнаёт больше, если взять ошибку в переменную: except ValueError as e даёт тип, сообщение и аргументы исключения.
Исключения · Урок 6
else и finally: два хвоста try
Два хвоста try: else работает при успехе, finally — при любом исходе. Разбираем порядок try, except, else, finally и место каждого блока.
Похожие уроки по темам
Подобраны автоматически по пересечению тем и ключевых слов.
asyncio · Урок 11
Исключения в конкурентном коде
Одна корутина с ValueError способна уронить весь gather. Флаг return_exceptions=True превращает исключения в значения списка — пачка выживает, а отчёт собирается целиком.
asyncio исключения gathergather обработка ошибок
pytest · Урок 9
Проверка исключений: pytest.raises
Хорошая функция на плохом входе не молчит, а бросает исключение — и тест обязан это проверять. pytest.raises делает это одной строкой внутри with.
pytest проверка исключенияpytest ValueError тест
Исключения · Урок 1
Что такое исключение: первая ошибка в программе
Первая ошибка в программе: читаем traceback за десять секунд — тип ошибки, место и сообщение. Разбираем четыре типовых падения на живом коде.
python исключенияисключения python