Перейти к содержанию

432 уроков, 26 библиотек и челлендж «Что выведет код?» — бесплатно, код прямо в браузере

Начать обучение
Урок 12 из 20 Начальный 15 мин 100 XP

ValueError в Python: разбор строк в числа

int('восемнадцать') — тип подходящий, а значение не помещается. Разбираем сообщение invalid literal, границы int и float и честную проверку isdigit.

Редакция Питоники

Данные из файлов, форм и API приходят строками, и первым делом их превращают в числа: int('17'), float('3.14'). Разбор ломается на живом вводе регулярно — пользователь набрал словами, файл пришёл с пустой строкой. Третий урок курса уже ловил эту ошибку в ветке except ValueError; теперь разберёмся, когда именно она возникает и что говорит её сообщение.

Разбор строки, которая не число

int на слове
age_text = 'восемнадцать'
age = int(age_text)
Программа падает, и последняя строка traceback читается: ValueError: invalid literal for int() with base 10: 'восемнадцать'. Тип операции верный — int умеет принимать строки; не подошло само значение.

Ошибка ValueError означает, что тип операции подходящий, а значение в него не помещается. Сравните с TypeError из мира несоответствий: там операция вообще не применима к такому типу, а здесь int со строками работает — просто из этой конкретной строки число не извлекается. Формулировка invalid literal означает «неверный литерал»: запись числа не выдержана.

Перебираем грязный ввод

три строки ввода: одна пустая
for text in ['17', 'восемнадцать', '']:
    try:
        print(repr(text), '->', int(text))
    except ValueError as e:
        print(repr(text), '->', type(e).__name__, '-', str(e))
Вывод
'17' -> 17
'восемнадцать' -> ValueError - invalid literal for int() with base 10: 'восемнадцать'
'' -> ValueError - invalid literal for int() with base 10: ''

Функция repr печатает строку в кавычках — удобно видеть, что именно разбиралось, особенно пустую строку, которая на экране выглядит как ничего. Сообщение ValueError в каждой ветке называет виновника: и слово словами, и пустота дают одну и ту же ошибку с разным значением внутри. int() строг к содержимому: пробелы по краям он прощает, но дробную запись и лишние слова не примет.

пробелы, точка, вставка внутри
for text in ['  42 ', '42.5', '4 2']:
    try:
        print(repr(text), '->', int(text))
    except ValueError as e:
        print(repr(text), '->', type(e).__name__, '-', str(e))
Вывод
'  42 ' -> 42
'42.5' -> ValueError - invalid literal for int() with base 10: '42.5'
'4 2' -> ValueError - invalid literal for int() with base 10: '4 2'

Пробелы по краям int обрезает сам, а вот точка — уже не его дело: '42.5' для целого числа неверная запись, даже хотя выглядит числом. Пустая строка между цифрами ломает разбор так же, как слово. Кстати, int('42.5') не округляет: чтобы получить дробное число, нужен float.

float и граница с TypeError
for text in ['3.14', 'abc']:
    try:
        print(repr(text), '->', float(text))
    except ValueError as e:
        print(repr(text), '->', type(e).__name__, '-', str(e))

try:
    int(['1', '2'])
except TypeError as e:
    print('список ->', type(e).__name__, '-', str(e))
Вывод
'3.14' -> 3.14
'abc' -> ValueError - could not convert string to float: 'abc'
список -> TypeError - int() argument must be a string, a bytes-like object or a real number, not 'list'

Граница между двумя ошибками видна в одном примере. Строка 'abc' — подходящий тип, неподходящее значение: ValueError. Список — неподходящий тип вовсе: TypeError с внятным перечислением того, что int принимает. Правило простое: ловить нужно ту ошибку, которая реально случится в вашем коде. Разбираете строки — ловите ValueError, а TypeError останется сигналом о баге.

Проверка заранее: isdigit

Второй способ жить с грязным вводом — спросить у строки, число ли она, до вызова int. У строк для этого есть метод isdigit:

что isdigit считает цифрами
candidates = ['123', '42.5', '-7', '', 'восемь']
for text in candidates:
    print(repr(text), 'isdigit:', text.isdigit())
Вывод
'123' isdigit: True
'42.5' isdigit: False
'-7' isdigit: False
'' isdigit: False
'восемь' isdigit: False

Метод строг: только чистые цифры от края до края. Минус цифрой не считается, поэтому '-7' не проходит проверку — хотя int его отлично разбирает. Точка тоже не цифра. Из-за такой строгости isdigit подходит не для всех вводов: возраст со знаком минус, цену с копейками — всё это отсеется.

безопасный разбор через isdigit
def safe_int(text, default=0):
    if text.isdigit():
        return int(text)
    return default

print(safe_int('42'))
print(safe_int('восемнадцать'))
print(safe_int('17', default=-1))
Вывод
42
0
17

Классический каркас: проверка пускает к int только чистые цифры, всё остальное получает умолчание. Удобно, пока ввод заведомо положительные целые. Как только появляются минусы или дроби, проверка начинает отбраковывать годные значения — и тут try с except ValueError честнее: он решает по факту, а не по предположению. Какой стиль выбрать — вопрос из четырнадцатого урока об EAFP.

Входisdigitint()
'123'Trueразбирает: 123
'-7'Falseразбирает: -7
'42.5'FalseValueError
''FalseValueError

Разбор ввода — самая разносторонняя тренировка для try и except: строки приходят пустыми, словами, с пробелами и минусами, и каждая форма по-своему проверяется. А когда нужно зафиксировать правило для разработчика, а не для пользователя — пригодится assert из восемнадцатого урока.

Что выведет код?

Сначала предскажи ответ в голове — это главный навык программиста.

try:
    print(int('10') + int('5'))
except ValueError:
    print('не разобралось')
for t in ['7', 'семь']:
    try:
        print(int(t) * 2)
    except ValueError:
        print('skip:', t)
print('-5'.isdigit())
try:
    print(int('-5'))
except ValueError:
    print('нет')
Проверь себя
0 / 7

1. Почему int('восемнадцать') даёт ValueError, а не TypeError?

2. Что вернёт int(' 42 ')?

3. Что произойдёт при int('42.5')?

4. Какая ошибка возникнет при int(['1', '2'])?

5. Что вернёт '-7'.isdigit()?

6. '²'.isdigit() возвращает True. Что вернёт int('²')?

7. Когда try с except ValueError надёжнее проверки isdigit?

Карточки терминов
Запомнено: 0 / 7
Практика

Для списка вводов ['15', 'двадцать', ' 30 '] в цикле напечатай «{repr строки} -> {число}», а если int падает с ValueError — «{repr строки} -> не число». Используй try и except внутри цикла.

practice.py
Вопросы и ответы по уроку

Почему int('abc') вызывает ValueError в Python?

int умеет принимать строки — тип операции верный, но из этой строки число не извлекается: запись не соответствует формату целого. Python сообщает ValueError: invalid literal for int() with base 10: 'abc', называя неудачное значение прямо в сообщении.

Урок 3: except ValueError — ловим конкретный тип

Как проверить строку перед int()?

Методом isdigit: строка проходит проверку, только если целиком состоит из цифр. Но метод строг: '-7' и '42.5' он отбракует, а '²' пропустит зря. Надёжнее оборачивать int в try и ловить ValueError — решение принимается по факту разбора.

Чем ValueError отличается от TypeError?

ValueError — значение неподходящее при верном типе: int('abc'). TypeError — тип не подходит вовсе: int(['1']), int(None). Если разбор строк падает с TypeError, в данные просочился не-строковый объект — это другой баг и другой обработчик.

Урок 18: assert и AssertionError

int(' 42 ') работает, а int('4 2') — нет. Почему?

int обрезает пробелы только по краям записи: ' 42 ' превращается в '42' и разбирается. Пробел внутри — часть значения, и такая запись для целого числа неверна: ValueError: invalid literal for int() with base 10: '4 2'.

Понравился урок? Сошлитесь на него

«Ошибка ValueError означает, что тип операции подходящий, а значение в него не помещается.»

Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.

TelegramVK

Что дальше

Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.

Похожие уроки по темам

Подобраны автоматически по пересечению тем и ключевых слов.