如何在 Python 3 中使用集合模块

作者选择了 COVID-19 救援基金作为 Write for Donations计划的一部分接受捐款。

介绍

Python 3 有许多内置的数据结构,包括tuples、字典和列表,数据结构为我们提供了组织和存储数据的方式。

在本教程中,我们将通过收藏模块中的三个类(LINK0)来帮助您使用tuples、字典和列表。我们将使用namedtuples来创建名称字段的tuples、defaultdict来简要地将信息组合到字典中,以及deque来高效地将元素添加到类似列表的对象的两侧。

对于本教程,我们将主要与我们需要修改的鱼类库存工作,因为鱼被添加到或从虚构的水族馆中删除。

前提条件

要充分利用本教程,建议您熟悉 tuple、字典和列表数据类型,无论是他们的语法,还是如何从中获取数据。

将命名字段添加到 Tuples

Python tuples 是元素的不可变或不可改变的顺序序列。Tuples 通常用于代表列数据;例如,来自 CSV 文件的行或来自 SQL 数据库的行。

个性化鱼类:

1("Sammy", "shark", "tank-a")

这个tuple由三个 string 元素组成。

虽然在某些方面有用,但这个标签并不清楚地表明它的每个字段代表了什么。实际上,元素0是一个名称,元素1是一个物种,元素2是持有库。

鱼鱼场的解释:

namespeciestank
Sammysharktank-a

该表清楚地表明,Tuple的三个元素中每一个都有一个明确的含义。

收藏模块中namedtuple允许您将明确的名称添加到一个tuple的每个元素中,以便在Python程序中明确这些含义。

让我们使用namedtuple来生成一个明确命名鱼的每个元素的类:

1from collections import namedtuple
2
3Fish = namedtuple("Fish", ["name", "species", "tank"])

「從集合輸入 namedtuple」讓您的Python程式存取「namedtuple」工廠函數. 「namedtuple()」函數呼叫返回一個與名稱「Fish」有關的類別。

我们可以使用类来代表以前的鱼:

1sammy = Fish("Sammy", "shark", "tank-a")
2
3print(sammy)

如果我们运行此代码,我们会看到以下输出:

1[secondary_label Output]
2Fish(name='Sammy', species='shark', tank='tank-a')

sammy是使用Fish类来实例化的。

sammy 的字段可以通过其名称或使用传统的 tuple 索引访问:

1print(sammy.species)
2print(sammy[1])

如果我们运行这两个打印呼叫,我们会看到以下输出:

1[secondary_label Output]
2shark
3shark

访问 .species 会返回与使用 [1] 访问 sammy 中的第二个元素相同的值。

收藏模块中使用namedtuple使您的程序更易于阅读,同时保持一个tuple的重要属性(它们是不可变的和顺序)。

此外,namedtuple工厂函数还为Fish的实例添加了几种额外的方法。

使用 ._asdict()将实例转换为字典:

1print(sammy._asdict())

如果我们运行打印,你会看到输出如下:

1[secondary_label Output]
2{'name': 'Sammy', 'species': 'shark', 'tank': 'tank-a'}

在「sammy」上调用 `.asdict()' 会返回一个字典,将三个字段名称的每个名称与相应的值相符。

Python 版本超过 3.8 可能会以稍微不同的方式输出此行. 例如,您可能会看到一个OrderedDict而不是这里所示的简单字典。

<$>[注] **注:**在Python中,带有领先的低分的方法通常被视为私有。(https://docs.python.org/3/library/collections.html#collections.namedtuple)(_asdict(), ._make(),._replace()等),然而, 是公开的

在字典中收集数据

通常在Python字典中收集数据是有用的。从收藏模块中默认字典可以帮助我们快速、简洁地在字典中汇集信息。

defaultdict 永远不会引出 KeyError. 如果一个密钥不存在,那么 defaultdict 只会插入并返回一个位数值:

1from collections import defaultdict
2
3my_defaultdict = defaultdict(list)
4
5print(my_defaultdict["missing"])

如果我们运行此代码,我们将看到如下的输出:

1[secondary_label Output]
2[]

'defaultdict' 插入并返回一个位数值,而不是扔一个 'KeyError'. 在这种情况下,我们将位数值作为列表。

相反,常规字典会将KeyError投放到缺少的密钥上:

1my_regular_dict = {}
2
3my_regular_dict["missing"]

如果我们运行此代码,我们将看到如下的输出:

1[secondary_label Output]
2Traceback (most recent call last):
3  File "<stdin>", line 1, in <module>
4KeyError: 'missing'

常规字典my_regular_dict在尝试访问不存在的密钥时提出KeyError

defaultdict的行为与常规字典不同,而不是在缺失的密钥上举起一个KeyError,defaultdict将无参数的位置值调用来创建一个新的对象,在这种情况下,list()将创建一个空的列表。

继续我们虚构的水族馆示例,让我们假设我们有一个代表水族馆库存的鱼列表:

1fish_inventory = [
2    ("Sammy", "shark", "tank-a"),
3    ("Jamie", "cuttlefish", "tank-b"),
4    ("Mary", "squid", "tank-a"),
5]

水族馆里有三条鱼,它们的名字、物种和持有箱在这三只鱼上都记载着。

我们的目标是根据坦克来组织我们的库存,我们想要知道每个坦克中存在的鱼类清单,换句话说,我们想要一个字典,将坦克-a地图到Sammy,Marytank-bJamie

我们可以使用默认字符号来按坦克组合鱼类:

 1from collections import defaultdict
 2
 3fish_inventory = [
 4    ("Sammy", "shark", "tank-a"),
 5    ("Jamie", "cuttlefish", "tank-b"),
 6    ("Mary", "squid", "tank-a"),
 7]
 8fish_names_by_tank = defaultdict(list)
 9for name, species, tank in fish_inventory:
10    fish_names_by_tank[tank].append(name)
11
12print(fish_names_by_tank)

运行此代码,我们会看到以下输出:

1[secondary_label Output]
2defaultdict(<class 'list'>, {'tank-a': ['Sammy', 'Mary'], 'tank-b': ['Jamie']})

「fish_names_by_tank」被声明为「默认字符号」,默认时将list()插入而不是KeyError。因为这保证了fish_names_by_tank中的每个键都将指向list,我们可以自由地呼叫.append(),以便在每个坦克列表中添加名称。

Defaultdict在这里帮助你,因为它减少了意想不到的KeyErrors的可能性。减少意想不到的KeyErrors意味着你的程序可以写得更清晰,并且有更少的行。

没有默认命令,循环体可能看起来更像这样:

1[label More Verbose Example Without defaultdict]
2...
3
4fish_names_by_tank = {}
5for name, species, tank in fish_inventory:
6    if tank not in fish_names_by_tank:
7      fish_names_by_tank[tank] = []
8    fish_names_by_tank[tank].append(name)

仅使用常规字典(而不是默认字典)意味着for循环体必须在fish_names_by_tank中始终检查给定的坦克是否存在。

在填充字典时,Defaultdict可以帮助削减锅炉板代码,因为它从不提出KeyError

使用 deque 有效地将元素添加到收藏的任何一侧

Python 可以不断地附加到列表(列表的长度对附加所需的时间没有影响),但在列表的开始时插入可能会更慢 - 随着列表的增加,需要的时间会增加。

Big O 标注方面,附加到列表是一个恒定的时间 O(1) 操作。

<$>[注] 注: 软件工程师经常使用所谓的大O符号来衡量程序的性能.当输入的大小对执行程序所需的时间没有影响时,它被称为在恒定时间或O(1)(Big O of 1)中运行。

有时,输入的大小会直接影响运行程序所需的时间。例如,插入Python列表的开始速度越慢,列表中的元素越多。

一般而言,O(1)程序比O(n)程序更快。

我们可以在Python列表的开头插入:

1favorite_fish_list = ["Sammy", "Jamie", "Mary"]
2
3# O(n) performance
4favorite_fish_list.insert(0, "Alice")
5
6print(favorite_fish_list)

如果我们运行以下操作,我们将看到如下结果:

1[secondary_label Output]
2['Alice', 'Sammy', 'Jamie', 'Mary']

列表中的.insert(index, object)方法允许我们在favorite_fish_list开始时插入Alice。值得注意的是,在列表开始时插入具有O(n)性能。

收藏模块中deque(发音deck)是一个类似列表的对象,允许我们在序列的开始或结束时以恒定时间(‘O(1)’)的性能插入项目。

插入一个项目在一个deque的开始:

1from collections import deque
2
3favorite_fish_deque = deque(["Sammy", "Jamie", "Mary"])
4
5# O(1) performance
6favorite_fish_deque.appendleft("Alice")
7
8print(favorite_fish_deque)

运行此代码,我们将看到以下输出:

1[secondary_label Output]
2deque(['Alice', 'Sammy', 'Jamie', 'Mary'])

我们可以使用先前存在的元素集合来实例化一个deque,在这种情况下是三种最喜欢的鱼类名称列表。称呼favorite_fish_deque附属左方法允许我们在我们的集合开始时以O(1)的性能插入一个项目。

<$>[注] **注:**虽然deque比列表更有效地将条目添加到序列的开始,但deque不会比列表更有效地执行所有操作。例如,在deque中访问随机项目具有O(n)性能,但访问列表中的随机项目具有O(1)性能。当重要的是快速插入或删除集合的两侧元素时,使用deque

结论

收藏模块是Python标准库的强大组成部分,允许您简洁、高效地处理数据。本教程涵盖了收藏模块提供的三个类别,包括namedtuple,defaultdictdeque

从这里,您可以使用收藏模块的文档(https://docs.python.org/3/library/collections.html#module-collections)来了解更多关于其他可用的类和实用程序。

Published At
Categories with 技术
Tagged with
comments powered by Disqus