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

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

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

KeyError в Python: ошибки словарей

Словарь не нашёл ключ — и честно сказал об этим KeyError. Разбираем объект ошибки, читаем из него ключ и выбираем между get, in и честным исключением.

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

Словарь — самая ходовая структура для данных из реального мира: настройки, ответы API, записи о пользователях. И самая частая ошибка при работе с ними — обращение к ключу, которого нет: опечатка, поле не пришло, ключ назван чуть иначе. Python не возвращает None и не молчит — он поднимает KeyError, и этот урок о том, как с ним обращаться.

Как воспроизводится KeyError

Прямое обращение по ключу в квадратных скобках — самый короткий путь к ошибке. Стоит ключу не найтись, и программа падает:

обращение к отсутствующему ключу
settings = {'theme': 'dark', 'lang': 'ru'}
print(settings['font'])
Программа падает с последней строкой traceback: KeyError: 'font'. Словарь называет отсутствующий ключ прямо в сообщении — сразу видно, чего именно ему не хватило.

KeyError принадлежит семье LookupError — ветке поиска из девятого урока об иерархии: искомое не нашлось. Его брат IndexError возникает при той же ситуации в списках, только там вместо ключа — номер. В сообщении KeyError печатается отсутствующий ключ в кавычках: читая traceback, вы первым делом видите виновника.

Ловим и разбираем объект ошибки

KeyError под контролем
settings = {'theme': 'dark', 'lang': 'ru'}

try:
    print(settings['font'])
except KeyError as e:
    print('поймали', type(e).__name__, 'для ключа', str(e))
Вывод
поймали KeyError для ключа 'font'

Исключение несёт ключ внутри себя: str(e) возвращает его строковое представление вместе с кавычками. Если кавычки мешают — например, ключ идёт в журнал, где уже есть своё оформление — берите данные напрямую из e.args, кортежа аргументов, с которыми исключение было создано:

ключ внутри исключения
d = {}
try:
    d['город']
except KeyError as e:
    print('args:', e.args)
    print('первый аргумент:', e.args[0])
Вывод
args: ('город',)
первый аргумент: город

e.args[0] — чистый ключ без кавычек: его можно сравнивать, подставлять в сообщения, писать в отчёт. Именно так ловля KeyError превращается из паники в обработку: программа знает, какого именно ключа не хватило, и решает, что с этим делать.

ловля в цикле: данные с пробелами
cities = {'Москва': 'Россия', 'Париж': 'Франция'}

for city in ['Москва', 'Рим', 'Париж']:
    try:
        print(city, '-', cities[city])
    except KeyError:
        print(city, '- в справочнике нет')
Вывод
Москва - Россия
Рим - в справочнике нет
Париж - Франция

Три способа жить с возможным отсутствием

Ловля — не единственный путь. У словарей есть собственные инструменты против отсутствия, и выбор между ними зависит от смысла ситуации. Первый и самый частый — метод get, который вместо падения возвращает значение по умолчанию:

get: умолчание вместо падения
settings = {'theme': 'dark', 'lang': 'ru'}

font = settings.get('font', 'Arial')
theme = settings.get('theme', 'light')
print('шрифт:', font)
print('тема:', theme)
Вывод
шрифт: Arial
тема: dark
in: отсутствие как отдельная ветка
settings = {'theme': 'dark'}

if 'lang' in settings:
    print('язык:', settings['lang'])
else:
    print('язык не задан — берём умолчание ru')
Вывод
язык не задан — берём умолчание ru

Если для отсутствующего ключа есть разумное значение по умолчанию, словарный get решает вопрос без исключений — подробнее о нём в уроке словарей о get. Если отсутствие — нормальная ветка логики, с отдельными действиями на «есть» и «нет», удобна проверка in из урока о проверке ключа. А try с except KeyError остаётся правильным выбором, когда отсутствие ключа — аномалия: без этого словаря программа не может работать, и молчаливая подстановка спрятала бы проблему.

СпособЗаписьКогда выбирать
get с умолчаниемsettings.get('font', 'Arial')есть разумное значение по умолчанию
проверка inif 'font' in settings:отсутствие — обычная ветка логики
try и except KeyErrortry: settings['font'] except KeyError:отсутствие — аномалия, нужен сигнал наверх

Ключ, которого нет, — самый рядовой случай из мира данных, и Python даёт на него три ответа. Какой из трёх выбрать при обходе коллекций с сомнительным содержимым — вопрос стиля, которому посвящён четырнадцатый урок об идиоме EAFP. А полный курс о словарях — от создания до частотных таблиц — ждёт в разделе Словари.

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

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

d = {'a': 1}
try:
    print(d['b'])
except KeyError as e:
    print('нет ключа', str(e))
d = {'x': 5}
print(d.get('y', 0) + d['x'])
d = {}
try:
    d['ключ']
except KeyError as e:
    print(e.args[0])
Проверь себя
0 / 6

1. Что происходит при обращении settings['font'], если ключа font в словаре нет?

2. Что вернёт str(e) в ветке except KeyError as e для ключа 'font'?

3. Чем settings.get('font', 'Arial') отличается от settings['font']?

4. Когда try с except KeyError — правильный выбор вместо get?

5. Кто родитель KeyError в иерархии исключений?

6. Словарь d = {'1': 'строка'}. Что произойдёт при обращении d[1]?

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

Дан словарь цен prices = {'хлеб': 40, 'молоко': 80}. Пройдись циклом по списку ['хлеб', 'сыр', 'молоко']: для каждой позиции напечатай «{товар} - {цена}», а если товара в словаре нет — поймай KeyError и напечатай «{товар} - нет в продаже».

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

Почему возникает KeyError в Python?

Словарь обратился по ключу, которого в нём нет: опечатка, другой регистр, лишний пробел или поле, не пришедшее из данных. Прямая скобка settings['ключ'] не выдумывает значений — она поднимает KeyError с этим ключом в сообщении.

Курс Словари: доступ по ключу и KeyError

Как исправить KeyError?

Зависит от ситуации: если есть разумное значение по умолчанию — читать через get: settings.get('font', 'Arial'). Если отсутствие — нормальная ветка логики — проверить ключ через in. Если отсутствие — аномалия — обернуть в try и поймать KeyError, а из объекта ошибки взять ключ через e.args[0].

Урок 4 курса словарей: get

Как получить отсутствующий ключ из объекта KeyError?

Исключение несёт ключ в аргументах: str(e) вернёт его с кавычками ('font'), а e.args[0] — чистую строку font. Так обработчик знает, какого именно ключа не хватило, и может подставить его в сообщение или журнал.

Чем KeyError отличается от IndexError?

Они братья: оба — дети LookupError. KeyError возникает, когда не нашёлся ключ словаря, IndexError — когда не нашёлся индекс списка. Суть одна — искомого нет, поэтому except LookupError ловит обе ошибки сразу.

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

«Python не возвращает None и не молчит — он поднимает KeyError, и этот урок о том, как с ним обращаться.»

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

TelegramVK

Что дальше

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

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

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