БэкендData и ИИPython

KeyError в Python: как проверить ключ словаря и исправить ошибку

Разбираем KeyError в Python на словарях: читаем traceback, проверяем реальные ключи, выбираем in, get, setdefault или обработку исключения.

Кодик

Автор

5 мин чтения

KeyError означает, что программа запросила у словаря ключ, которого в нём нет. Посмотрите имя ключа в последней строке traceback и сравните его с dictionary.keys(). Дальше выберите поведение по смыслу: обязательный ключ читайте через квадратные скобки и исправляйте входные данные, необязательный получайте через get, а наличие проверяйте оператором in. Не подставляйте значение по умолчанию, если отсутствие должно считаться ошибкой.

Ошибка часто появляется при чтении JSON, подсчёте элементов или обращении к настройкам: вчера поле name было во всех записях, а сегодня одна запись пришла без него. Ещё один частый случай проще: в словаре хранится user_name, а код просит username. Python не угадывает похожие написания и показывает отсутствующий ключ в кавычках.

1Смотрим traceback

Берём последнюю строку своего кода и точное представление отсутствующего ключа.

2Печатаем структуру

Проверяем тип объекта, keys(), регистр, пробелы и вложенность.

3Выбираем контракт

Обязательное поле исправляем, необязательное обрабатываем явно и предсказуемо.

Почему словарь выдаёт KeyError и что написано в traceback

Словарь сопоставляет уникальные хешируемые ключи со значениями. Выражение profile["city"] требует, чтобы ключ city существовал прямо сейчас. Если его нет, Python возбуждает KeyError. Последняя строка traceback обычно содержит сам ключ, а строка выше указывает место обращения. Наличие ключа не зависит от того, истинно ли его значение: ключ с None, нулём или пустой строкой всё равно существует.

СловарьВыражениеРезультат
{"name": "Ada"}d["name"]"Ada"
{"name": "Ada"}d["city"]KeyError: city
{"city": None}"city" in dTrue
{"city": None}d.get("city")None
{}d.get("city", "не указан")"не указан"

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

Проверяйте наличие, а не значение. Условие if d.get("score") считает отсутствием и ключ со значением 0. Для проверки структуры используйте if "score" in d.

Находим ключ с помощью keys, repr и маленького примера

При диагностике не печатайте гигантский JSON целиком. Выведите тип объекта, ключи нужного уровня и точное представление подозрительной строки через repr. Оно показывает невидимые пробелы и разницу между строкой и числом. Вложенный словарь проверяйте по одному уровню, потому что одинаковый KeyError может возникнуть и на внешнем, и на внутреннем обращении.

profile = {
    "name": "Ada",
    "stats": {"score": 0},
    "city ": "London",
}

requested = "city"
print(type(profile).__name__)
print(list(profile.keys()))
print(repr(requested))
print([repr(key) for key in profile])

if requested in profile:
    print(profile[requested])
else:
    print(f"Нет ключа {requested!r}")

# Безопасно читаем необязательное поле
language = profile.get("language", "не указан")
print(language)
Что обнаружит этот вывод
  • Ключ "city " содержит завершающий пробел и не равен "city".
  • Нулевой score существует, хотя в логическом условии само значение считается ложным.
  • get позволяет явно задать подпись для действительно необязательного language.

Дерево выбора между in get setdefault и try except для словаря
Метод выбирают по смыслу отсутствия и по тому, должен ли словарь измениться.

Когда использовать скобки, get, setdefault и try except

Эти инструменты не взаимозаменяемы. Квадратные скобки честно сообщают о нарушении обязательной структуры. get удобен для необязательного чтения без изменения словаря. setdefault возвращает значение и одновременно создаёт отсутствующий ключ. try/except KeyError полезен, когда чтение является основной операцией и редкое отсутствие нужно обработать рядом.

ИнструментКогда подходитВажная особенность
d[key]Ключ обязан существоватьОшибка быстро раскрывает плохие данные
key in dНужны разные веткиОтличает отсутствие от ложного значения
d.get(key, default)Необязательное чтениеНе изменяет словарь
d.setdefault(key, default)Нужно создать контейнерИзменяет словарь
try/except KeyErrorРедкое отсутствие при основном чтенииОбрабатывайте только узкий участок
defaultdictМного накоплений по новым ключамСоздаёт значение при обращении
Default не должен скрывать испорченные данные. Если API обязано прислать id заказа, data.get("id", 0) создаст ложный заказ с id 0. Здесь лучше оставить явную ошибку или валидировать ответ.

Практика: считаем слова без KeyError

Счётчик частот показывает разницу между чтением и созданием ключа. При первом появлении слова записи ещё нет, поэтому прямое увеличение counts[word] += 1 падает. Исправить можно через get, setdefault, defaultdict или Counter. Для начала полезно реализовать вариант с get: в нём явно виден старт с нуля.

Соберите и проверьте счётчик
  1. Возьмите строку "код учит код проверяет код" и разделите её методом split().
  2. Создайте пустой словарь counts = {}.
  3. Для каждого слова выполняйте counts[word] = counts.get(word, 0) + 1.
  4. Проверьте результат: у слова «код» должно быть 3, у двух остальных слов по 1.
  5. Повторите задачу через обычный if word in counts и сравните читаемость.
  6. Добавьте нормализацию lower() и удаление знаков препинания до подсчёта, не во время доступа к словарю.
Ожидаемый результат
  • Новые слова получают начальное значение 0 и увеличиваются до 1 без исключения.
  • Повторное слово использует существующее значение и в конце даёт 3.
  • В коде видно, что отсутствие ключа для счётчика является нормальным первым состоянием.

Поток подсчёта слов в словаре Python с начальным значением ноль
Для счётчика новый ключ является нормальным состоянием и получает стартовое значение.

Почему KeyError возвращается после быстрого исправления

Обычно причина находится в границе данных: CSV содержит заголовок с пробелом, JSON имеет другую вложенность, пользователь ввёл разный регистр или один объект отличается от остальных. Без проверки схемы замена скобок на get лишь переносит ошибку в арифметику, шаблон или базу. Сначала сформулируйте, какие ключи гарантированы.

Все скобки механически заменены на get

Следующая операция получает None и падает уже как TypeError или AttributeError, дальше от причины.

Проверяется truthiness

if d.get(key) путает отсутствие с 0, False, пустой строкой и пустым списком.

Не замечены пробелы и регистр

"Name", "name" и "name " являются тремя разными ключами.

Перепутаны уровни JSON

Поле может находиться в data["user"]["name"], а не рядом с user. Проверяйте keys на каждом уровне.

except перехватывает слишком большой блок

Внутри может возникнуть другой KeyError. Оставьте в try только конкретное обращение, отсутствие которого ожидается.

Зафиксируйте минимальную схему входа. Перед основной логикой проверьте обязательные ключи и типы. Тогда ошибка сообщает о плохом входе один раз, а не возникает случайно глубоко в программе. Технические детали сверены с официальной документацией Python о встроенных исключениях.
Короткие ответы
Что показывает KeyError в последней строке?

Обычно точное представление ключа, который не найден в mapping.

Чем key in d отличается от d.get(key)?

in проверяет наличие, а get возвращает значение или default.

Изменяет ли get словарь?

Нет. setdefault и defaultdict могут создавать отсутствующие записи.

Почему if d.get("score") неверно для проверки ключа?

Значение 0 существует, но условие воспримет его как ложное.

Когда квадратные скобки лучше get?

Когда ключ обязателен и его отсутствие означает ошибку входных данных или программы.

Научитесь читать структуру до обращения к ней

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

Дополните тему материалом о словарях Python простыми словами, затем разберите AttributeError и TypeError при смешивании строк и чисел.