跳至主要内容

Python KeyError 异常及其修复方法

学习在 Python 中有效处理 KeyError 异常的关键技巧,例如异常处理与错误预防。
· 6分钟 阅读

使用 AI 探索

即便是经验最丰富的程序员也会犯错。对 Python 程序员来说,这些错误大致分为两类:错误(errors)和异常(exceptions)。

错误主要与语法有关,必须修复后程序才能运行。异常则发生在代码语法正确,但程序在运行时被某些情况中断。 

在本教程中,我们将介绍新手和有经验的程序员都会遇到的一种常见异常:KeyError。我们将详解 Python 的 KeyError,并提供示例与多种处理该类异常的技术。若您想系统学习如何识别并修复 Python 中的错误,也可查看我们的Python 异常与错误处理教程。 

什么是 KeyError 异常?

在 Python 中,KeyError 是内置异常之一,用于处理多种常见错误情形。KeyError 可以理解为更通用的 LookupError 异常的子类之一(另一子类为 IndexError),它们都是 Exception 类的对象。

具体来说,当程序尝试访问字典中不存在的键时,就会引发 KeyError。字典是一种以键-值对存储数据的数据结构,字典中的值通过键来访问——这也正是 KeyError 名称的由来。 

Python KeyError 的常见原因与示例

让我们看一个包含国家及其首都的字典示例:

dictionary_capitals = {'Madrid': 'Spain',
 'Lisboa': 'Portugal', 'London': 'United Kingdom'}

要在字典中查询信息,需要在方括号内指定键,Python 会返回其关联的值。

dictionary_capitals['Madrid']
'Spain'

如果尝试访问字典中不存在的键,Python 会给出 KeyError 异常的错误信息。 

dictionary_capitals['Rome']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'Rome'

当我们尝试在其他采用字典形式的 Python 映射对象中访问不存在的键时,也会遇到 KeyError。例如,os.environ 对象返回一个字典,以键-值形式存储用户的环境变量。

在下面的代码中,我们尝试访问 os.environ 字典中键 USERS 对应的值。因为 USERS 不在 os.environ 中,代码触发了 KeyError。

#Calling a non-existent environmental variable
os.environ['USERS']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/os.py", line 679, in __getitem__
    raise KeyError(key) from None
KeyError: 'USERS'

事实是,字典在 Python 中非常常见。另一个例子是更抽象的数据结构——哈希表(hashmap)。想进一步了解,请查看我们的Python 哈希表指南教程。

处理 Python KeyError 异常

接下来探索处理 KeyError 的方法。我们有两种策略:要么预防 KeyError,要么通过异常处理捕获 KeyError。 

预防 KeyError

如前所述,当我们访问不存在的键时,Python 会抛出 KeyError。为避免这种情况,可以使用字典的 .get() 方法,使代码更具容错性。使用该方法时,若键不存在,Python 会返回 None 而不是 KeyError。这是一个可取的做法。

print(dictionary_capitals.get('Prague'))
None

或者,我们也可以在访问前检查键是否存在。这种预防异常的方式被称为“先看一眼再说”(Look Before You Leap,简称 LBYL)。在这种情况下,可以使用 if 语句检查键是否存在;若不存在,则在 else 分支中进行处理。

capital = "Prague"
if capital in dictionary_capitals.keys():
    value = dictionary_capitals[capital]
else:
    print("The key {} is not present in the dictionary".format(capital)) 

通过异常处理捕获 KeyError

第二种方法被称为“请求原谅比征得许可更容易”(Easier to Ask Forgiveness Than Permission,EAFP)。EAFP 实际上是 Python 中更常见的异常处理风格。 

采用 EAFP 编码风格意味着我们默认假设键是有效存在的,如果假设不成立再捕获异常。LBYL 依赖 if/else 语句,而 EAFP 则依赖 try/except 子句。 

在下面的示例中,我们不再检查键是否存在,而是直接尝试访问目标键。如果由于某种原因键不存在,就在 except 子句中捕获 KeyError 并妥善处理。

capital = "Prague"
try:
     value = dictionary_capitals[capital]
except KeyError:
     print("The key {} is not present in the dictionary".format(capital)) 

进阶的 Python KeyError 管理

使用 defaultdict 实现自动键处理

我们看到,只要尝试访问字典中不存在的键,Python 就会返回 KeyError。此前介绍的 .get() 方法是一种容错方式,虽然可用,但并非最优。

Collections 模块提供了 defaultdict,这是一种更优的字典条目处理方式。与标准字典在访问缺失键时抛出 KeyError 不同,defaultdict 允许我们指定一个默认值,在找不到键时返回。下面的代码展示了如何从 Collections 导入 defaultdict,并用 lambda 函数创建默认值。若您对 lambda 函数感到困惑,可查看我们的Python lambda 教程了解更多。

from collections import defaultdict 

# Defining the dict 
capitals = defaultdict(lambda: "The key doesn't exist") 
capitals['Madrid'] = 'Spain'
capitals['Lisboa'] = 'Portugal'
print(capitals['Madrid']) 
print(capitals['Lisboa']) 
print(capitals['Ankara'])
Spain
Portugal
The key doesn't exist

有意触发 KeyError

最后需要说明的是,在某些情况下,出于约束目的,有意抛出 KeyError 也是合适的做法。

我们知道,很多函数依赖特定数据来执行其操作。手动触发 KeyError 可以确保在缺失必要数据时,函数以清晰、可预期的方式失败。此时,随 KeyError 提供的错误信息能准确描述输入的问题,有助于调试。

下面用一个示例来说明。这里我们创建了一个以字典为参数的函数。为使函数正常工作,用户必须提供具有正确键的字典。之所以创建这样的函数,是因为我们希望在键缺失时立刻得知问题,而不是让错误在代码中继续传播。 

def my_function(my_dict): 
    if 'critical_key' not in my_dict: 
        raise KeyError('critical_key is missing from the dictionary. Please modify the dictionary')

结语

希望本文对您有所帮助。KeyError 是 Python 开发者最常遇到的异常之一。掌握错误与异常处理的技巧,是成为优秀 Python 程序员的关键一步。想了解更多函数式编程技术与编码基础,请查看我们的 Python 编程 与 Python 基础技能路径。另外,您也可以阅读我们的Python 集合与集合论教程,拓展相关概念。 

Javier Canales Luna

作者

Javier Canales Luna

我是一名自由职业的数据分析师,与全球各地的公司和组织合作开展数据科学项目。我也担任数据科学讲师,拥有2年以上授课经验。我经常用英语和西班牙语撰写与数据科学相关的文章,其中一些已发表在 DataCamp、Towards Data Science 和 Analytics Vidhya 等知名网站上。作为一名具有政治学与法律背景的数据科学家,我的目标是在公共政策、法律与技术的交汇处开展工作,借助思想的力量推动创新解决方案与叙事,帮助我们应对紧迫挑战,尤其是气候危机。我认为自己是自学成才、不断进步的人,坚定支持跨学科合作。学习新事物永远不嫌晚。

主题