KeyError в Python: ошибки словарей
Словарь не нашёл ключ — и честно сказал об этим KeyError. Разбираем объект ошибки, читаем из него ключ и выбираем между get, in и честным исключением.
Редакция Питоники
Словарь — самая ходовая структура для данных из реального мира: настройки, ответы API, записи о пользователях. И самая частая ошибка при работе с ними — обращение к ключу, которого нет: опечатка, поле не пришло, ключ назван чуть иначе. Python не возвращает None и не молчит — он поднимает KeyError, и этот урок о том, как с ним обращаться.
Как воспроизводится KeyError
Прямое обращение по ключу в квадратных скобках — самый короткий путь к ошибке. Стоит ключу не найтись, и программа падает:
settings = {'theme': 'dark', 'lang': 'ru'}
print(settings['font'])
KeyError принадлежит семье LookupError — ветке поиска из девятого урока об иерархии: искомое не нашлось. Его брат IndexError возникает при той же ситуации в списках, только там вместо ключа — номер. В сообщении KeyError печатается отсутствующий ключ в кавычках: читая traceback, вы первым делом видите виновника.
Ловим и разбираем объект ошибки
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, который вместо падения возвращает значение по умолчанию:
settings = {'theme': 'dark', 'lang': 'ru'}
font = settings.get('font', 'Arial')
theme = settings.get('theme', 'light')
print('шрифт:', font)
print('тема:', theme)
шрифт: Arial тема: dark
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') | есть разумное значение по умолчанию |
| проверка in | if 'font' in settings: | отсутствие — обычная ветка логики |
| try и except KeyError | try: 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])
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]?
Дан словарь цен prices = {'хлеб': 40, 'молоко': 80}. Пройдись циклом по списку ['хлеб', 'сыр', 'молоко']: для каждой позиции напечатай «{товар} - {цена}», а если товара в словаре нет — поймай KeyError и напечатай «{товар} - нет в продаже».
Почему возникает 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-канал или свой блог — так о проекте узнают новые читатели.
Что дальше
Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.
Что читать дальше
Словари · Урок 3
Доступ по ключу и KeyError
Что происходит при доступе по несуществующему ключу: настоящий traceback, построчный разбор KeyError, отлов через try/except и случаи, когда падение — правильное поведение.
Словари · Урок 4
get: безопасное чтение из словаря
Метод get читает словарь без KeyError: без ключа вернёт None, со вторым аргументом — запасное значение. Сравниваем с квадратными скобками, разбираем арифметику с None и молчание get.
Исключения · Урок 14
EAFP против LBYL в Python: два стиля проверки
Look Before You Leap — смотри, прежде чем прыгнуть; Easier to Ask Forgiveness than Permission — проще извиниться, чем спросить. Одна задача, два стиля, один вердикт.
Похожие уроки по темам
Подобраны автоматически по пересечению тем и ключевых слов.
json · Урок 8
Путь к значению: достаём данные из глубины
Цепочки data["user"]["name"] и data["items"][0]["price"], KeyError у отсутствующих полей и get с умолчанием — спуск по типичному ответу API без падений.
keyerror python jsondict get python умолчание
Словари · Урок 7
in: проверка ключа в словаре
Оператор in отвечает на вопрос «есть ли такой ключ»: проверка перед доступом вместо KeyError, not in, грабли с проверкой значений и причина скорости поиска.
python dict inоператор in python словарь
Исключения · Урок 13
FileNotFoundError в Python: файл не нашёлся
open('нет-такого-файла.txt') — и программа падает. Ловим FileNotFoundError, читаем имя файла из объекта ошибки и создаём файлы прямо в примерах.
python os.path.exists проверкаpython ошибка открытия файла