简介
转载:四种高性能数据类型,Python collections助你优化代码、简洁任务。
一般而言,Python 中的 collections
模块是用于存储列表、字典、元组以及集等数据集合的容器。这些容器嵌入在 Python 中,可以实现开箱即用。collections
模块提供了额外的高性能数据类型,它们可以优化代码,让一些任务变得更加简洁。
Counter
官方文档。
Counter
是 dictionary
对象的子类。collections
模块中的 Counter()
函数会接收一个诸如 list
或 tuple
的迭代器,然后返回一个 Counter dictionary
。这个 dictionary
的键是该迭代器中的唯一元素,每个键的值是迭代器元素的计数。
首先,我们需要从 collections
包中导入 Counter
:
1 | from collections import Counter |
如果要创建一个 Counter
对象,我们也要像对待其他对象类一样,先将它分配给一个变量,而传递给 Counter
对象的惟一变量即是迭代器。
1 | lst = [1, 2, 3, 3, 2, 1, 1, 1, 2, 2, 3, 1, 2, 1, 1] |
如果我们使用简单的 print
函数把这个 Counter
打印出来,则会得到一些与 dictionary
稍微类似的输出:
1 | Counter({1: 7, 2: 5, 3: 3}) |
你可以用这些键值访问任何 Counter
项。这与从标准的 Python dictionary
中获取元素的方法完全相同。
1 | lst = [1, 2, 3, 3, 2, 1, 1, 1, 2, 2, 3, 1, 2, 1, 1] |
most_common()
函数
目前来说,Counter
对象中最有用的函数是 most_common()
。当它应用于一个 Counter
对象时,会返回一个 list
,这个 list
包含了前 N
个常见的元素及其计数,它们按照常见度降序排列。
1 | lst = [1, 2, 3, 3, 2, 1, 1, 1, 2, 2, 3, 1, 2, 1, 1] |
上述代码会打印出以下 tuples
的 list
。
1 | [(1, 7), (2, 5)] |
每个 tuple
的首个元素是 list
中的唯一项,第二个元素是计数值。对于「获取 list
中前 3 常见的元素及其计数」这样的问题,这会是一种快速且简单的方法。
defaultdict
官方文档。
defaultdict
的工作方式和平常的 Python dictionary
完全相同,只是当你试图访问一个不存在的键时,它不会报错,而是会使用默认值初始化这个键。默认值是根据在创建 defaultdict
对象时作为参数输入的数据类型自动设置的。下面的代码就是一个例子。
1 | from collections import defaultdict |
在上面的示例中,传递给 defaultdict
对象的默认值是 int
。然后每个键得到了一个值,也就是「Bob」和「Katie」各获得了一个数字。但是在最后一行,我们试着访问了一个尚未定义的键,即「Sara」。
在普通 dictionary
中,这种操作会报错。但是使用 defaultdict
时,将自动为「Sara」初始化一个新键,其值 0 对应于我们的 int
数据类型。因此,最后一行可以把这「Bob」、「Katie」和「Sara」以及对应的值都打印出来。
1 | defaultdict(<class 'int'>, {'Bob': 1, 'Katie': 2, 'Sara': 0}) |
如果我们改用 list
来初始化我们的 defaultdict
,也就是 names_dict = defaultdict(list)
,那么「Sara」的值将被初始化成一个空列表 []
,打印来的内容就变成了:
1 | defaultdict(<class 'int'>, {'Bob': 1, 'Katie': 2, 'Sara': []}) |
deque
官方文档。
queue
是计算机科学中的一种基础数据架构,它遵循先进先出(First-In-First-Out,FIFO)的原则。简单来说,就是添加到 queue
中的第一个对象也必须是要第一个删除。我们只能在 queue
前面插入内容,也只能从后面删除内容——无法对中间内容进行操作。
collections
库中的 deque
对该功能进行了优化。这个方法的一个关键特性是保持队列长度一直不变,也就是说,如果你将 queue 的最大大小设置为 10,那么 deque
将根据 FIFO 原则添加和删除元素,以保持 queue
的最大大小为 10。这是迄今为止 Python 中使用 queue
的最好方法了。
再来看一个例子。我们先创建了一个 deque
对象,然后用从 1 到 10 的整数初始化它。
1 | from collections import deque |
在上面的代码中,我们首先初始化 deque
,指定它的最大长度为 10。然后,我们通过 for loop
将值插入到 queue
中。注意这里我们使用了与常见 Python list
相同的方式填充 queue
。最后,我们把结果打印出来。
1 | deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], maxlen=10) |
因为我们的 queue
被设置成 maxlen=10
,而 loop
值添加了 10 个元素,所以这个 queue
包含了从 1 到 10 的所有数字。现在我们来看一下如果继续向里面添加数字会发生什么。
1 | for i in range(10, 15): |
在上述代码中,我们又向 queue
中添加了 5 个元素——数字 11 到 15。但是我们的 queue
只能有 10 个元素,所以它需要删除一些元素。因为 queue
必须服从 FIFO 原则,所以它删掉了前五个插入到 queue
中的元素,按照插入顺序就是 [1, 2, 3, 4, 5]
。打印的结果如下:
1 | deque([6, 7, 8, 9, 10, 11, 12, 13, 14, 15], maxlen=10) |
namedtuple
官方文档。
当你使用 Python 创建一个常规 tuple
时,其元素都是通用的,而且没有被命名。这使得你必须记住每个 tuple
元素的精确索引。namedtuple
就可以解决这个问题。
namedtuple()
可以返回一个 tuple
,该 tuple
中的每个位置都有固定名称,而且 namedtuple
对象也有通用名称。要使用 namedtuple
,需要先为其创建一个模板。下面的代码创建了一个名为「Person」的 namedtuple
模板,其属性为「name」、「age」和「job」。
1 | from collections import namedtuple |
上述代码很容易理解,我们为 namedtuple
初始化了一个「Person」模板,并初始化了其所有的属性。上述代码最后的打印结果是:
1 | Person(name='Mike', age=30, job='Data Scientist') |
因此,namedtuple
让 tuple
的使用更简单、更可读且更有组织性。