在 Python 中应该如何以及何时使用 Defaultdict?

深入理解 Python 字典与 defaultdict 的应用

本教程将引导你探索如何利用 Python 集合模块中的 defaultdict,更有效地处理 Python 字典操作中可能出现的 KeyError 异常。

在 Python 中,字典作为一种内置的数据结构,以键值对的形式存储数据,允许通过键来访问对应的值。

然而,在实际应用中,当代码执行过程中存在多个字典被修改的情况时,经常会遭遇 KeyError 异常。本文将介绍几种处理此类异常的策略。

本教程的核心内容包括:

  • 解析 KeyError 的本质及其产生的原因。
  • 学习多种处理 KeyError 的方法。
  • 掌握如何通过 Python 的 defaultdictdict 类的子类)优雅地处理缺失的键。

让我们开始学习吧!

Python 中的 KeyError 是什么?

在定义 Python 字典时,你需要注意以下关键点:

  • 确保键的唯一性,避免重复键。
  • 当使用可迭代对象作为键时,优先选择如元组等不可变集合。

只有当键实际存在于字典中时,才能通过该键访问对应的值,否则会导致 KeyError

考虑以下名为 books_authors 的字典,其中键为书名,值为作者姓名。

你可以在 Python REPL 中跟随本教程进行练习。

books_authors = {
    'Deep Work':'Cal Newport',
    'Hyperfocus':'Chris Bailey',
    'Pivot':'Jenny Blake',
    'The Happiness Equation':'Neil Pasricha'
}
  

通过书名(键)可以访问作者姓名:

books_authors['Hyperfocus']
#'Chris Bailey'
  

使用字典对象的 items() 方法可以遍历所有键值对:

for book,author in books_authors.items():
  print(f"'{book}' by {author}")
  
# 'Deep Work' by Cal Newport
# 'Hyperfocus' by Chris Bailey
# 'Pivot' by Jenny Blake
# 'The Happiness Equation' by Neil Pasricha
  

当尝试访问字典中不存在的键时,Python 解释器会抛出 KeyError 异常。如下例子中,尝试访问 'Grit''non-existent-key' 会引发错误。

books_authors['Grit']
  
# ---------------------------------------------------------------------------
# KeyError                                  Traceback (most recent call last)
# <ipython-input-6-e1a4486f5ced> in <module>
# ----> 1 books_authors['Grit']
#
# KeyError: 'Grit'
  
books_authors['non-existent-key']
  
# ---------------------------------------------------------------------------
# KeyError                                  Traceback (most recent call last)
# <ipython-input-7-a3efd56f69e5> in <module>
# ----> 1 books_authors['non-existent-key']
#
# KeyError: 'non-existent-key'
  

那么,在 Python 中如何有效地处理 KeyError 呢?

接下来,我们将学习几种处理方法。

如何在 Python 中处理 KeyErrors

以下是处理 KeyError 的几种常用方法:

  • 使用 if-else 条件语句
  • 使用 try-except 语句块
  • 使用字典的 .get() 方法

#1. 使用 If-Else 条件语句

使用 if-else 条件语句是处理 KeyError 的一种简单直接的方式。

if-else 语句的基本结构如下:

if condition:
  # 条件为真时执行
else:
  # 条件为假时执行
    
  • 如果条件为 True,则执行 if 代码块中的语句。
  • 如果条件为 False,则执行 else 代码块中的语句。

在这个示例中,条件用于检查指定的键是否存在于字典中。

如果键存在,in 运算符返回 True,执行 if 代码块打印值。

key = 'The Happiness Equation'
if key in books_authors:
  print(books_authors[key])
else:
  print('抱歉,该键不存在!')

# 输出
# Neil Pasricha
    

如果键不存在,in 运算符返回 False,执行 else 代码块,并打印提示信息。

key = 'non-existent-key'
if key in books_authors:
  print(books_authors[key])
else:
  print('抱歉,该键不存在!')

# 输出
# 抱歉,该键不存在!
    

#2. 使用 Try-Except 语句

另一种处理 KeyError 的常用方法是使用 try-except 语句。

代码示例:

key = 'non-existent-key'
try:
  print(books_authors[key])
except KeyError:
  print('抱歉,该键不存在!')
    
  • try 代码块尝试获取指定键的值。
  • 如果键不存在,解释器会抛出 KeyError 异常,该异常会被 except 代码块捕获并处理。

#3. 使用 .get() 方法

在 Python 中,可以使用内置的字典方法 .get() 来处理缺失的键。

.get() 方法的基本语法是:dict.get(key, default_value),其中 dict 是一个有效的 Python 字典对象。

– 如果键存在,.get() 方法返回对应的值。
– 否则,返回指定的默认值。

在这个示例中,我们定义了一个键的列表,并尝试从 books_authors 字典中检索它们对应的值。

我们使用 .get() 方法,并将 “不存在” 作为默认值。

keys = ['Grit','Hyperfocus','Make Time','Deep Work']
for key in keys:
  print(books_authors.get(key,'不存在'))
    

在上述代码中:

  • 对于 books_authors 字典中存在的键, .get() 方法返回相应的值。
  • 当键不存在时,如 “Grit” 和 “Make Time”, .get() 方法返回默认值 “不存在”。
# 输出
# 不存在
# Chris Bailey
# 不存在
# Cal Newport
    

以上方法都可以帮助我们处理 KeyError,但它们都相对繁琐,需要显式地处理缺失的键。通过使用 defaultdict,我们可以简化这一过程。

Python 中的 defaultdict

defaultdictdict 类的子类,继承了 Python 字典的所有行为,并能原生处理缺失的键。

defaultdict 是一种容器数据类型,内置于 Python 标准库的 collections 模块中。

因此,你需要从 collections 模块导入 defaultdict:

from collections import defaultdict
  

使用 defaultdict 的一般语法是:

defaultdict(default_factory)
  

你可以将 intfloatlist 等可调用对象指定为 default_factory 属性。如果没有为 default_factory 提供值,则默认为 None

当尝试访问字典中不存在的键时,会触发 __missing__() 方法,它会根据 default_factory 推断一个默认值并返回。

总结一下:

  • 在 Python 中,当键不存在时,defaultdict 返回一个默认值。
  • 它还会将键值对(键,默认值)添加到字典中,之后你可以修改这个值。

Python 默认字典示例

接下来,我们将通过几个示例来了解 Python defaultdict 的工作原理。

Python 中具有默认整数值的 defaultdict

首先,从 collections 模块导入 defaultdict

from collections import defaultdict
import random
    

创建一个 defaultdict 实例 prices

prices = defaultdict(int)
    

我们使用水果列表中的项目作为键来填充 prices 字典。从 price_list 中随机采样值作为对应的值。

price_list = [10,23,12,19,5]
fruits = ['apple','strawberry','pomegranate','blueberry']

for fruit in fruits:
  prices[fruit] = random.choice(price_list)
    

让我们看一下 prices defaultdict 中的键值对。

print(prices.items())
    
# dict_items([('apple', 12), ('blueberry', 19), ('pomegranate', 5), ('strawberry', 10)])
    

与普通的 Python 字典一样,你可以通过键访问 prices defaultdict 的值:

prices['apple']
# 23
    

现在,让我们尝试访问一个不存在的水果的价格,例如 “orange”。我们看到它返回默认值零。

prices['orange']
# 0
    

如果我们打印字典,我们会看到添加了一个新键 “orange”,默认整数值为零。

print(prices.items())
    
# dict_items([('apple', 12), ('blueberry', 19), ('pomegranate', 5), ('strawberry', 10), ('orange', 0)])
    

Python 中以列表为默认值的 defaultdict

让我们将 students_majors 定义为以列表为默认值的 defaultdict。专业的名字是键,值是攻读该专业的学生列表,如数学、经济学、计算机科学等。

from collections import defaultdict
students_majors = defaultdict(list)
    

如果我们尝试访问 ‘Economics’ 对应的学生列表,defaultdict 返回一个空列表;不会有 KeyError

students_majors['Economics']
# []
    

现在我们有一个映射到 “Economics” 专业的空列表。 我们可以使用列表方法 .append() 向列表中添加元素。

students_majors['Economics'].append('Alex')
    

students_majors defaultdict 中,为 “Economics” 专业创建了一个条目。

print(students_majors)
    
# defaultdict(<class 'list'>, {'Economics': ['Alex']})
    

可以向经济学专业添加更多学生,或者添加新的专业!

students_majors['Economics'].append('Bob')
students_majors['Math'].append('Laura')
print(students_majors)
    
# defaultdict(<class 'list'>, {'Economics': ['Alex', 'Bob'], 'Math': ['Laura']})
    

总结

希望本教程能帮助你理解如何在 Python 中使用 defaultdict。在学习完本教程的代码示例后,你可以在项目中尝试使用 defaultdict 作为首选数据结构。

本教程的核心知识点总结如下:

  • 在使用 Python 字典时,你可能会遇到 KeyError
  • 可以使用条件语句、try-except 语句块或 .get() 方法处理此类错误。collections 模块中的 defaultdict 可以简化 KeyError 的处理。
  • 使用 defaultdict(default_factory) 可以创建一个 defaultdict,其中 default_factory 是一个有效的可调用对象。
  • 当键在 defaultdict 中不存在时,默认值(从 default_factory 推断)和键会被添加到 defaultdict 中。

接下来,请查阅有关 Python 映射函数的教程。