Python DefaultDict 使用入门

582 阅读5分钟

在我们使用 Python 处理数据时,字典是最常用的数据结构。它易于使用,用途广泛。

比如需要计算字符串中某些字符出现次数?可以使用字典!

再比如统计一份足球运动员个人信息的相关数据?可以使用字典!

不过,字典也并非完美。在许多时候,当我们尝试通过键获取数据时会遇到大量 KeyErrors 错误,处理起来也是一件令人沮丧的事情。

处理这些错误需要额外编写代码。这不仅降低了可读性,还增加了复杂性。如果要处理的数据量过大,这个问题可能会失控。

collections 模块可以帮助我们很好的解决这个问题。collections 模块是 Python 标准库的一部分,它提供了一些非常实用的容器类型,可以帮助我们更方便地处理特定需求。

今天我们将探讨的是 defaultdict,要充分理解这个数据容器,你应该对 Python 普通字典类型有一定的了解。

使用 DefaultDict 简化代码

在进入今天的主题之前,我们先来看一种情况。我想创建一个字典,统计 “Mississippi” 这个单词中所有字母的个数。

下面是我使用标准字典的实现方法:

letters = {}

for letter in "Mississippi":
    if letter not in letters:
        letters[letter] = 1
    else:
        letters[letter] +=1
    
print(letters)
# {'M': 1, 'i': 4, 's': 4, 'p': 2}

很简单。这个程序

  • 遍历字符串。
  • 每次迭代时,检查字典中是否有该字母的条目。
  • 如果存在,则在当前值的基础上加一。
  • 如果不存在,则创建新条目并将初始值设置为一。

这个例子非常简单,但使用 defaultdict 仍然有改进余地。让我们看看该如何做:

from collections import defaultdict

letters = defaultdict(int)

for letter in "Mississippi":
    letters[letter] += 1
    
print(letters)
# defaultdict(<class 'int'>, {'M': 1, 'i': 4, 's': 4, 'p': 2})

仔细观察以上代码,你应该能够注意到,现在所有的条件语句都不见了。代码变的更容易阅读了,但我们的程序仍然得到了相同的结果。

这就是 defaultdict 的好处,接下来让我们更进一步分析这个数据容器。

探索 DefaultDict 数据容器

其实 defaultdict 的工作原理很简单:如果试图访问或更改一个不存在的键,它就会创建一个具有给定默认值的新条目。

在上面的示例中,我们首先创建了一个没有任何条目的空 defaultdict。对于每一个字母,defaultdict 都会创建一个条目。由于我们使用 int 作为默认值生成器,因此新条目的初始值为 0。

在 DefaultDict 中设置默认值

当你创建 defaultdict 的实例时,需要将一个无参数的函数(或可调用对象)作为 default_factory 参数传递给 defaultdict 的构造函数。当你尝试访问不存在的键时,defaultdict 会自动调用这个 default_factory 函数,并将返回值作为该键的默认值。

例如,我们可以使用内置函数 int() 来创建 defaultdict

d1 = defaultdict(int)

当试图访问一个不存在的条目时,它会调用 int() 函数,并将其返回值作为新条目的初始值,即 0。

d1 = defaultdict(int)

d1[“Adding an entry!”]

Print(d1)# defaultdict(<class 'int'>, {'Adding an Entry!': 0})

探索 DefaultDict 的更多使用技巧

现在我们已经知道了 defaultdict 的基本用法,让我们探索其更多使用技巧吧。

让我们回到前面的例子。我想知道所有字母所在位置的索引,该如何做?我们可以使用内置函数 list() 作为 default_factory 参数创建 defaultdict 实例,这样我们就可以记录所有索引了。

from collections import defaultdict

my_word = "Mississippi"

d1 = defaultdict(list)

for index, letter in enumerate(my_word):
    d1[letter].append(index)
        
print(d1)
# defaultdict(<class 'list'>, {'M': [0], 'i': [1, 4, 7, 10], 's': [2, 3, 5, 6], 'p': [8, 9]})

这个例子看起来有点不同,但思路仍然相同。步骤如下

  • 首先创建一个 defaultdict 实例,使用内置函数 list() 作为 default_factory 参数。
  • 迭代字符串 "Mississippi"。
  • 如果字母对应的条目不存在,defaultdict 将会使用空列表作为初始值创建它。
  • 最后,使用 append 方法将索引添加到字母对应条目列表中。

如前所述,default_factory 是一个不带参数的函数,这意味着我们不仅可以使用内置函数,还可以使用自定义函数,只要我们的自定义函数不包含参数即可。

from collections import defaultdict

def return_hello():
    return "Hello!"
    
d1 = defaultdict(return_hello)

d1[1]
d1[2]
d1[3]

print(d1)
# defaultdict(<function return_hello at 0x0000014FC5D28DC0>, {1: 'Hello!', 2: 'Hello!', 3: 'Hello!'})

以上代码定义了一个返回 “Hello!” 字符串的简单函数,并将其作为 default_factory 参数创建一个 defaultdict 实例。当我们尝试访问其不存在的条目时,defaultdict 将会调用我们的自定义函数来生成默认值。

结语

在本教程中,我们介绍了 defaultdict,它是 Python 标准库 collections 模块中的一个数据容器,它提供了一个具有默认值的字典。

defaultdict 接受一个 default_factory 参数,该参数为字典中不存在的键提供一个默认值的生成器。default_factory 参数可以是内置函数,如 intlist,也可以是自定义函数,如上面的 return_hello 函数。