Исключения Python KeyError и как их обработать

Исключения Python KeyError и как их обработать

Исключение PythonKeyError - распространенное исключение, с которым сталкиваются новички. Знание, почемуKeyError может быть повышено, и некоторые решения, позволяющие предотвратить остановку вашей программы, - важные шаги к совершенствованию как программиста Python.

К концу этого руководства вы будете знать:

  • Что обычно означает PythonKeyError

  • Где еще вы могли бы увидетьKeyError в стандартной библиотеке

  • Как обращаться сKeyError, когда вы его видите

Free Bonus:Click here to get our free Python Cheat Sheet, который показывает вам основы Python 3, такие как работа с типами данных, словарями, списками и функциями Python.

Что обычно означаетKeyError в Python

PythonKeyErrorexception - это то, что возникает, когда вы пытаетесь получить доступ к ключу, которого нет вdictionary (dict).

official documentation в Python говорит, чтоKeyError вызывается при обращении к ключу сопоставления и не обнаруживается в сопоставлении. Отображение - это структура данных, которая отображает один набор значений в другой. Наиболее распространенным отображением в Python является словарь.

PythonKeyError является типом исключенияLookupError и означает, что возникла проблема с получением ключа, который вы искали. Когда вы видитеKeyError, семантическое значение состоит в том, что искомый ключ не может быть найден.

В приведенном ниже примере вы можете увидеть словарь (ages) с возрастом трех человек. Когда вы пытаетесь получить доступ к ключу, которого нет в словаре, возникаетKeyError:

>>>

>>> ages = {'Jim': 30, 'Pam': 28, 'Kevin': 33}
>>> ages['Michael']
Traceback (most recent call last):
  File "", line 1, in 
KeyError: 'Michael'

Здесь попытка доступа к ключу'Michael' в словареages приводит к подъемуKeyError. В нижней части трассировки вы получите соответствующую информацию:

  • Тот факт, что aKeyError был повышен

  • Ключ, который не удалось найти,'Michael'

Строка от последней к последней говорит вам, какая строка вызвала исключение. Эта информация более полезна, когда вы выполняете код Python из файла.

Note: Когда в Python возникает исключение, это делается с помощьюtraceback. Трассировка предоставляет вам всю необходимую информацию, чтобы определить, почему возникло исключение и что его вызвало.

Умение читать трассировку Python и понимание того, что он говорит вам, крайне важно для улучшения как программиста на Python.

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

 1 # ages.py
 2
 3 ages = {'Jim': 30, 'Pam': 28, 'Kevin': 33}
 4 person = input('Get age for: ')
 5 print(f'{person} is {ages[person]} years old.')

Этот код примет имя, которое вы указали в приглашении, и попытается определить возраст этого человека. Все, что вы вводите в командной строке, будет использоваться как ключ к словарюages в строке 4.

Повторяя неудачный пример выше, мы получаем еще одну трассировку, на этот раз с информацией о строке в файле, из которой возникаетKeyError:

$ python ages.py
Get age for: Michael
Traceback (most recent call last):
File "ages.py", line 4, in 
  print(f'{person} is {ages[person]} years old.')
KeyError: 'Michael'

Программа не работает, когда вы даете ключ, которого нет в словаре. Здесь последние несколько строк трассировки указывают на проблему. File "ages.py", line 4, in <module> сообщает вам, в какой строке какого файла возникло исключениеKeyError. Тогда вам показывают эту линию. Наконец, исключениеKeyError предоставляет недостающий ключ.

Итак, вы можете видеть, что последняя строка трассировкиKeyError сама по себе не дает вам достаточно информации, но строки перед ней могут значительно приблизить вас к пониманию того, что пошло не так.

Note: Как и в примере выше, большинство других примеров в этом руководстве используютf-strings, которые были введены в Python 3.6.

Где еще вы могли бы увидеть PythonKeyError в стандартной библиотеке

В подавляющем большинстве случаев вызывается PythonKeyError, потому что ключ не найден в словаре или подклассе словаря (например,os.environ).

В редких случаях вы также можете увидеть его поднятым в других местах стандартной библиотеки Python, например, в модулеzipfile, если элемент не найден в ZIP-архиве. Однако эти места сохраняют то же семантическое значение PythonKeyError, которое не находит запрошенный ключ.

В следующем примере вы можете увидеть использование классаzipfile.ZipFile для извлечения информации о ZIP-архиве с помощью.getinfo():

>>>

>>> from zipfile import ZipFile
>>> zip_file = ZipFile('the_zip_file.zip')
>>> zip_file.getinfo('something')
Traceback (most recent call last):
File "", line 1, in 
File "/path/to/python/installation/zipfile.py", line 1304, in getinfo
  'There is no item named %r in the archive' % name)
KeyError: "There is no item named 'something' in the archive"

Это не похоже на поиск по словарю. Вместо этого это вызовzipfile.ZipFile.getinfo() вызывает исключение.

Трассировка также выглядит немного иначе, поскольку предоставляется немного больше информации, чем просто отсутствующий ключ:KeyError: "There is no item named 'something' in the archive".

И последнее, на что следует обратить внимание, это то, что строки, которая поднялаKeyError, нет в вашем коде. Он находится в кодеzipfile, но предыдущие строки трассировки указывают, какие строки в вашем коде вызвали проблему.

Когда вам нужно поднять PythonKeyError в собственном коде

Бывают случаи, когдаyou to raise является исключением PythonKeyError в вашем собственном коде. Это можно сделать, используя ключевое словоraise и вызывая исключениеKeyError:

raise KeyError(message)

Обычно отсутствующим ключом являетсяmessage. Однако, как и в случае с пакетомzipfile, вы можете указать немного больше информации, чтобы помочь следующему разработчику лучше понять, что пошло не так.

Если вы решили поднять PythonKeyError в своем собственном коде, просто убедитесь, что ваш вариант использования соответствует семантическому значению исключения. Следует отметить, что искомый ключ не может быть найден.

Как обращаться с PythonKeyError, когда вы его видите

Когда вы сталкиваетесь сKeyError, есть несколько стандартных способов справиться с этим. В зависимости от вашего варианта использования, некоторые из этих решений могут быть лучше, чем другие. Конечная цель - предотвратить возникновение неожиданных исключенийKeyError.

Обычное решение:.get()

ЕслиKeyError возникает из-за неудачного поиска ключа словаря в вашем собственном коде, вы можете использовать.get() для возврата либо значения, найденного в указанном ключе, либо значения по умолчанию.

Как и в предыдущем примере поиска возраста, в следующем примере показан лучший способ получить возраст из словаря, используя ключ, указанный в приглашении:

 1 # ages.py
 2
 3 ages = {'Jim': 30, 'Pam': 28, 'Kevin': 33}
 4 person = input('Get age for: ')
 5 age = ages.get(person)
 6
 7 if age:
 8     print(f'{person} is {age} years old.')
 9 else:
10     print(f"{person}'s age is unknown.")

Здесь в строке 5 показано, как можно получить значение возраста изages с помощью.get(). Это приведет к тому, что переменнаяage будет иметь значение возраста, найденное в словаре для предоставленного ключа, или значение по умолчанию,None в этом случае.

На этот раз вы не получите исключениеKeyError из-за использования более безопасного метода.get() для получения возраста, а не попытки получить доступ к ключу напрямую:

$ python ages.py
Get age for: Michael
Michael's age is unknown.

В приведенном выше примере выполненияKeyError больше не возникает, если указан неверный ключ. Ключ'Michael' не найден в словаре, но, используя.get(), мы получаем возвращаемыйNone, а не поднятыйKeyError.

Переменнаяage будет иметь либо возраст человека, найденный в словаре, либо значение по умолчанию (по умолчаниюNone). Вы также можете указать другое значение по умолчанию в вызове.get(), передав второй аргумент.

Это строка 5 из приведенного выше примера с другим возрастом по умолчанию, указанным с помощью.get():

age = ages.get(person, 0)

Здесь вместо'Michael', возвращающегоNone, он вернет0, потому что ключ не найден, а возвращаемое значение по умолчанию теперь0.

Редкое решение: проверка на наличие ключей

Есть моменты, когда вам нужно определить наличие ключа в словаре. В этих случаях использование.get() может не дать вам правильную информацию. ПолучениеNone, возвращенного из вызова.get(), может означать, что ключ не был найден или что значение, найденное для ключа в словаре, на самом делеNone.

Со словарем или подобным словарю объектом вы можете использовать операторin, чтобы определить, присутствует ли ключ в отображении. Этот оператор вернет логическое (True илиFalse) значение, указывающее, найден ли ключ в словаре.

В этом примере вы получаете словарьresponse в результате вызова API. Этот ответ может иметь значение ключаerror, определенное в ответе, что указывает на то, что ответ находится в состоянии ошибки:

 1 # parse_api_response.py
 2 ...
 3 # Assuming you got a `response` from calling an API that might
 4 # have an error key in the `response` if something went wrong
 5
 6 if 'error' in response:
 7     ...  # Parse the error state
 8 else:
 9     ...  # Parse the success state

Здесь есть разница в проверке наличия ключаerror вresponse и получении значения по умолчанию из ключа. Это редкий случай, когда вы на самом деле ищете, если ключ находится в словаре, а не то, каково значение этого ключа.

Общее решение:tryexcept

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

Вы можете использовать блокtryexcept в том же примере, что и раньше, но на этот раз предоставив сообщение по умолчанию, которое будет напечатано, еслиKeyError будет поднят в нормальном случае:

 1 # ages.py
 2
 3 ages = {'Jim': 30, 'Pam': 28, 'Kevin': 33}
 4 person = input('Get age for: ')
 5
 6 try:
 7     print(f'{person} is {ages[person]} years old.')
 8 except KeyError:
 9     print(f"{person}'s age is unknown.")

Здесь вы можете увидеть нормальный случай в блокеtry печати имени и возраста человека. Резервный случай находится в блокеexcept, где, еслиKeyError поднимается в нормальном случае, тогда резервный вариант должен напечатать другое сообщение.

Блочное решениеtryexcept также является отличным решением для других мест, которые могут не поддерживать.get() или операторin. Это также лучшее решение, еслиKeyError поднимается из кода другого человека.

Вот пример, снова использующий пакетzipfile. На этот раз блокtryexcept дает нам способ остановить исключениеKeyError от остановки программы:

>>>

>>> from zipfile import ZipFile
>>> zip = ZipFile('the_zip_file.zip')
>>> try:
...     zip.getinfo('something')
... except KeyError:
...     print('Can not find "something"')
...
Can not find "something"

Поскольку классZipFile не предоставляет.get(), как это делает словарь, вам необходимо использовать решениеtryexcept. В этом примере вам не нужно заранее знать, какие значения допустимы для передачи.getinfo().

Заключение

Теперь вы знаете несколько распространенных мест, где может возникнуть исключениеKeyErrorв Python, и несколько отличных решений, которые можно использовать, чтобы предотвратить остановку вашей программы.

Теперь, когда вы в следующий раз увидите поднятыйKeyError, вы поймете, что это, вероятно, просто неверный поиск ключа словаря. Вы также сможете найти всю информацию, необходимую для определения источника ошибки, взглянув на последние несколько строк трассировки.

Если проблема заключается в поиске ключа словаря в вашем собственном коде, вы можете переключиться с доступа к ключу непосредственно в словаре на использование более безопасного метода.get() с возвращаемым значением по умолчанию. Если проблема не в вашем собственном коде, то использование блокаtryexcept - лучший вариант для управления потоком кода.

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