news 2026/10/12 12:41:03

python语言中的常用容器(集合)示例详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
python语言中的常用容器(集合)示例详解

前言


先说一个措辞问题。这个标题里的「容器」和「集合」其实是两个层级的概念,不该并列。容器(container)是上位概念,指任何用来装一组对象的类型,Python 内置的list、tuple、dict、set、str都属于容器;而集合(set)是其中一个具体的类型,特指无序、元素唯一的那种容器。标题写成「常用容器(集合)」,容易让人误以为「容器就等于集合」。准确的表述应该是「常用容器,含集合」。


顺带说一句:英文里collection也常被翻译成「集合」,那是指更宽泛的「一组对象」,和set这个具体类型不是一回事。collections模块名字里的那个collections就是前者。


本文按「四类内置容器逐个过一遍 + 横向对比 + 各自示例」的结构,把list、tuple、dict、set一次讲清,重点放在set上。


一、四个内置容器总览


先看一张总表,把四者的关键差别对齐。




容器字面量写法可变有序可哈希成员判断典型场景



list[1, 2]是是否O(n)有序、可变、允许重复的数据

tuple(1, 2)否是元素可哈希时是O(n)固定记录、当字典键

dict{"a": 1}是3.7 起保证插入顺序否O(1) 平均键值映射

set{1, 2}是否否O(1) 平均去重、存在性判断



几个必须记住的细节:



  • {}是空字典,空集合只能写set();空列表是[],空元组是()。

  • dict和set的成员判断都是哈希定位,平均 O(1);list和tuple要逐个比。

  • dict的键必须可哈希,set的元素也必须可哈希,它们的内核是同一套哈希表。

  • tuple不可变,所以元素都可哈希时整体可哈希;list可变,永远不可哈希。


二、list:最常用的可变序列


列表按位置组织数据,支持下标、切片、增删改,允许重复元素。


# 适用于 Python 3.8+
fruits = ["apple", "banana", "cherry"]
fruits.append("date") # 尾部追加
fruits.insert(0, "avocado") # 指定位置插入
fruits.remove("banana") # 按值删除,不存在则抛 ValueError

print(fruits) # ['avocado', 'apple', 'cherry', 'date']
print(fruits[1]) # apple
print(sorted(fruits)) # 返回排序后的新列表

# 注意 sort() 原地排序并返回 None
ret = fruits.sort()
print(ret) # None
print(fruits) # 排序后的列表本身

最后三行是一个高频坑:list.sort()原地排序、返回None,而内置函数sorted()返回新列表。把fruits = fruits.sort()写出来,fruits就变成None了。


三、tuple:不可变序列


元组和列表的读取方式一样,区别在于不能改。


# 适用于 Python 3.8+
point = (3, 5)
print(point[0], point[1]) # 3 5

# 单元素元组的逗号不能省
one = (1,)
print(type(one).__name__) # tuple
print(type((1)).__name__) # int,这个是整数

# 解包
x, y = point
print(x, y) # 3 5

元组只有count和index两个专属方法。因为它不可变,所以能被当成字典的键、放进集合,这是列表做不到的。


四、dict:键值映射


字典保存键到值的映射,从 3.7 起保证插入顺序(3.6 只是实现细节,不要依赖 3.6 的行为)。


# 适用于 Python 3.8+
student = {"name": "小明", "age": 12}
student["grade"] = 6 # 新增键
student["age"] = 13 # 覆盖已有键

print(student["name"]) # 小明
print(student.get("score")) # None,键不存在返回 None
print(student.get("score", 0)) # 0,可以给默认值
print(list(student.keys())) # ['name', 'age', 'grade'],插入顺序

d[key]在键不存在时抛KeyError,d.get(key)返回None——这是两者最重要的区别。另外,从 3.9 起有|合并运算符(PEP 584),3.8 及更早版本要用{**a, **b}。


# 适用于 Python 3.9+
other = {"city": "北京"}
merged = student | other
print(sorted(merged.keys())) # ['age', 'city', 'grade', 'name']

# 适用于 Python 3.8,替代 3.9 的 | 合并运算符
other = {"city": "北京"}
merged = {**student, **other}
print(sorted(merged.keys())) # ['age', 'city', 'grade', 'name']

五、set:无序且唯一


集合放进去的元素唯一、无序。它的两个核心用途是去重和快速判断存在。


# 适用于 Python 3.8+
# 去重
tags = ["python", "web", "python", "async"]
unique = set(tags)
print(sorted(unique)) # ['async', 'python', 'web']

# 空集合必须用 set()
empty = set()
print(type(empty).__name__) # set

# 成员判断,平均 O(1)
print("web" in unique) # True

# 集合运算
a = {"python", "async", "web"}
b = {"python", "packaging"}
print(sorted(a & b)) # ['python'],交集
print(sorted(a | b)) # 并集
print(sorted(a - b)) # 差集

集合要求元素可哈希:数字、字符串、bytes、frozenset可以;tuple在内部元素都可哈希时可以;list、dict、set不行。


# 适用于 Python 3.8+
print(set((1, 2, 3))) # {(1, 2, 3)},元组可哈希
print(set([1, 2])) # {1, 2},列表是「输入」而不是「元素」

try:
bad = {[1, 2], [3, 4]}
except TypeError as e:
print("报错:", e) # unhashable type: 'list'

# 换成元组就合法,也能表示「集合的集合」
good = {(1, 2), (3, 4)}
print(len(good)) # 2

这两段的对比值得琢磨:set(某列表)是把列表当输入,集合会逐个取出列表里的元素,所以它当然可以是不可哈希的东西;而{某列表}是把列表当元素,这时才要求可哈希。


六、横向对比示例


下面用同一份数据分别用四种容器处理,直观感受取舍。


# 适用于 Python 3.8+
raw = ["alice", "bob", "alice", "carol"]

as_list = list(raw) # 保留全部,含重复
as_tuple = tuple(raw) # 同上,但不可变
as_counts = {} # 统计出现次数
for name in raw:
as_counts[name] = as_counts.get(name, 0) + 1
as_set = set(raw) # 只留唯一值

print(as_list) # ['alice', 'bob', 'alice', 'carol']
print(as_tuple[0]) # alice
print(as_counts) # {'alice': 2, 'bob': 1, 'carol': 1}
print(sorted(as_set)) # ['alice', 'bob', 'carol']

# 顺序敏感的去重(3.7 起 dict 保序)
ordered_unique = list(dict.fromkeys(raw))
print(ordered_unique) # ['alice', 'bob', 'carol']

逐行推演:raw有四个元素,alice出现两次。as_list原样保留。as_counts用get(name, 0) + 1累加,得到每个名字的次数。as_set去掉重复,只剩三个。最后的dict.fromkeys(raw)按顺序建字典,键就是去重后的名字,再转回列表就得到保序去重结果——这是「既要去重又要保序」的标准写法,依赖的是字典 3.7 起保证插入顺序这条官方承诺。


七、实战:统计标签并比较两组


# 适用于 Python 3.8+
def tag_stats(rows):
"""rows 是 (文章标题, 标签列表) 的列表,统计每个标签出现次数。"""
counts = {}
for title, tag_list in rows:
for tag in tag_list:
counts[tag] = counts.get(tag, 0) + 1
return counts

def common_tags(rows_a, rows_b):
"""找出两组文章共有的标签。"""
a = {tag for _, tags in rows_a for tag in tags}
b = {tag for _, tags in rows_b for tag in tags}
return sorted(a & b), sorted(a - b), sorted(b - a)

if __name__ == "__main__":
group_a = [("t1", ["python", "web"]), ("t2", ["python", "test"])]
group_b = [("t3", ["python", "packaging"])]

stats = tag_stats(group_a)
print(sorted(stats.items())) # [('python', 2), ('test', 1), ('web', 1)]

shared, only_a, only_b = common_tags(group_a, group_b)
print("共有:", shared) # 共有: ['python']
print("只在 A:", only_a) # 只在 A: ['test', 'web']
print("只在 B:", only_b) # 只在 B: ['packaging']

逐行推演:tag_stats用字典累加每个标签的出现次数,counts.get(tag, 0)在标签首次出现时给 0。common_tags用集合推导式把两组的标签分别收集成集合,再做交集和两个方向的差集。sorted(stats.items())把键值对按元组排序后打印。整段只用到 dict 和 set 的基础用法。


常见坑点


坑点 1:把{}当成空集合。❌s = {}; s.add(1)——{}是空字典,抛AttributeError: 'dict' object has no attribute 'add'。 ✅ 空集合写s = set()。


坑点 2:把list.sort()的返回值赋回去。❌items = items.sort()——原地排序返回None,items变成None。 ✅ 直接items.sort();要新列表就用items = sorted(items)。


坑点 3:dict用[]取不存在的键。❌d["missing"]——抛KeyError。 ✅ 允许缺省时用d.get("missing")或带默认值的d.get("missing", 0)。


坑点 4:以为集合保持插入顺序。❌list(set(["b", "a"]))期待得到["b", "a"]——集合无序。 ✅ 保序去重用list(dict.fromkeys(["b", "a"])),得到["b", "a"]。


坑点 5:把不可哈希对象放进集合或当字典键。❌{[1, 2]}或d = {[1, 2]: "x"}——抛TypeError: unhashable type: 'list'。 ✅ 换成元组或字符串:{(1, 2)}、d = {(1, 2): "x"}。


坑点 6:以为set(某列表)会因为列表不可哈希而报错。❌ 看到「集合元素必须可哈希」就以为set([1, 2])非法——它完全合法,参数是被迭代的对象,不是元素。 ✅ 分清「作为输入」和「作为元素」:set([1, 2])得到{1, 2};{[1, 2]}才报错。


坑点 7:用+拼接集合。❌{1} + {2}——集合不支持+,抛TypeError。 ✅ 用并集:{1} | {2}或{1}.union({2}),得到{1, 2}。


坑点 8:以为dict和set的in也很快却用错了对象。❌"x" in ["a", "b", ...]对长列表反复判断——这是 O(n) 扫描。 ✅ 先把列表转成集合再判断:"x" in set(big_list),之后每次判断平均 O(1)。


总结




需求选哪个关键理由



有序、会增删改list可变、支持下标与切片

固定不变的一组值tuple不可变,可当键和集合元素

键到值的映射dict平均 O(1) 按键查找,3.7 起保序

去重 / 判断存在 / 集合运算set元素唯一,平均 O(1) 成员判断

需要可哈希的集合frozenset不可变所以可哈希



「容器」是一个大类,「集合」只是其中一员。选容器时按顺序问三个问题:需要键值映射吗(dict)?需要去重或纯存在性判断吗(set)?需要之后修改吗(list / tuple)?三个问题问完,选型基本就定了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/12 12:39:45

Python三维点云激光分类:从LAS数据到建筑树木语义标签的完整实现

简介:这是一份基于Python实现的三维点云激光分类项目源码,面向计算机、通信、人工智能、自动化等专业的学生与从业者,可用于毕业设计、课程大作业或期末课程设计,帮助解决点云场景中建筑、树木等地物自动分类的问题。压缩包共15个…

作者头像 李华
网站建设 2026/10/12 12:39:00

ITIL4发布计划如何避免假交付:从流程到落地的实战指南

我对“发布计划”四个字又爱又恨。做了十多年一线运维,我见过太多“假交付”的团队:ITIL4发布计划文档写得很完整,范围、时间、回滚、责任人样样齐全,审批全过,上线还是炸了。复盘时你会发现,风险登记册全是…

作者头像 李华
网站建设 2026/10/12 12:36:05

Python函数进阶:从参数机制到装饰器与生成器

要是有人问我,Python 里哪个知识点“学会了但用不好”,我第一个想到的就是函数。语法层面的东西谁都会——def 一敲、冒号一打、缩进一填,一个函数就出来了,可真到项目里设计函数时,很多人的代码写得……自己过两周都看…

作者头像 李华
网站建设 2026/10/12 12:34:16

蓝桥杯Python抽奖题:优惠券收集问题的期望与取模实现

蓝桥杯省赛 Python 组每年都会出那么一两道"把经典模型包装成故事"的题目,2025 年第十六届省赛这道《抽奖》就是典型。我拿到题面扫了一眼,第一反应是用 random 库疯狂模拟,好在这个念头只持续了两秒钟——等看到数据范围就彻底清醒…

作者头像 李华
网站建设 2026/10/12 12:30:18

HomebrewCN 脚本解析:国内镜像加速 Homebrew 安装与避坑指南

简介:这份资源是面向国内 macOS 用户的 Homebrew 国内安装脚本合集,主要解决官方源下载缓慢甚至无法访问的问题。包内共 12 个文件,以 5 个 sh 脚本为主,涵盖安装、卸载、Git 环境准备等环节,另有 3 个 md 说明文档、2…

作者头像 李华