小白学 Python
AI 编程指南GitHub
© 2026 小白学 Python · 基于 walter201230/Python 教程
课程目录关于本站联系方式隐私政策GitHub

Dict 与 Set

完整讲解 · 8 段教学·配 7 道练习题·预计 30 分钟

本页是本章的通读版,可直接读完全部讲解。想动手写代码、跑判分,去 闯关模式。

教学 01 / 08

第三节:Dict 与 Set

上一节学完了 List 与 Tuple。这一节再学两种容器类型——

  • dict(字典):key → value 映射,按 key 极速查找
  • set(集合):无序、不重复元素的集合,最适合「去重」和「成员判断」

学完这两种,再加上前面的 list 和 tuple,Python 最常用的数据结构就齐了。

教学 02 / 08

一、字典(Dictionary)

1、什么是 dict(字典)

上一章节,我们学习了列表(List)和元组(tuple)来表示有序集合。

而我们在讲列表时,用 list 来存储用户的姓名:

python到闯关页运行这段 →
name = ['一点水', '两点水', '三点水', '四点水', '五点水']

那么如果我们为了方便联系这些童鞋,要把电话号码也添加进去,该怎么做呢?

用 list 可以这样子解决:

python到闯关页运行这段 →
name = [['一点水', '131456780001'], ['两点水', '131456780002'], ...]

但是这样很不方便。如果用列表来存储这些,列表越长,我们查找起来耗时就越长。

这时候就可以用 dict(字典)来表示了。Python 内置了字典(dict),全称 dictionary,使用键-值(key-value) 存储,具有极快的查找速度(如果学过 Java,dict 就相当于 Java 中的 Map)。

python到闯关页运行这段 →
name = {'一点水': '131456780001', '两点水': '131456780002', '三点水': '131456780003'}

2、dict(字典)的创建

字典是另一种可变容器,且可存储任意类型对象。

字典的每个键值(key: value)对用冒号 : 分割,每个对之间用逗号 , 分割,整个字典包括在花括号 {} 中:

python到闯关页运行这段 →
d = {key1: value1, key2: value2}

注意:键必须是唯一的(重复会以最后一个为准),值可以重复。键必须是不可变类型(数字、字符串、tuple 都行,list 不行)。

python到闯关页运行这段 →
dict1 = {'liangdianshui': '111111', 'twowater': '222222', '两点水': '333333'}
dict2 = {'abc': 1234, 1234: 'abc'}
print(dict1)
print(dict2)
练习 1 / 7创建一个 dict去闯关页做这题 →
教学 03 / 08

3、访问 dict(字典)

我们用 dict 来存了通讯录,怎么按名字快速查到电话呢?直接用中括号 + key:

python到闯关页运行这段 →
name = {'一点水': '131456780001', '两点水': '131456780002', '三点水': '131456780003'}

print(name['两点水'])

输出的结果:

131456780002

可以看到,如果你知道某个人的名字(key),就能很快查到他对应的电话号码(value)。

⚠️ 注意:如果字典中没有这个键,直接 dict[key] 会报错(KeyError)。如果不确定 key 是否存在,更安全的写法是 dict.get(key)——找不到时返回 None,不报错。

python到闯关页运行这段 →
name.get('六点水')   # None,不会报错
name['六点水']       # KeyError!
练习 2 / 7按 key 取值去闯关页做这题 →
教学 04 / 08

4、修改 dict(字典)

向字典添加新内容、修改值,都是用 key 直接赋值:

python到闯关页运行这段 →
dict1 = {'liangdianshui': '111111', 'twowater': '222222', '两点水': '333333'}
print(dict1)

# 新增一个键值对
dict1['jack'] = '444444'
print(dict1)

# 修改键值对(key 已存在时会覆盖)
dict1['liangdianshui'] = '555555'
print(dict1)

输出的结果:

{'liangdianshui': '111111', 'twowater': '222222', '两点水': '333333'}
{'liangdianshui': '111111', 'twowater': '222222', '两点水': '333333', 'jack': '444444'}
{'liangdianshui': '555555', 'twowater': '222222', '两点水': '333333', 'jack': '444444'}

5、删除 dict(字典)

通过 del 可以删除某个 key 对应的元素,clear() 可以清空整个字典:

python到闯关页运行这段 →
dict1 = {'liangdianshui': '111111', 'twowater': '222222', '两点水': '333333'}

# 通过 key 值,删除对应的元素
del dict1['twowater']
print(dict1)

# 删除字典中的所有元素
dict1.clear()
print(dict1)

输出的结果:

{'liangdianshui': '111111', '两点水': '333333'}
{}

6、常用函数和方法

方法/函数描述
len(d)元素个数
d.keys()所有 key
d.values()所有 value
d.items()所有 (key, value) 对
d.get(key)安全取值,找不到返回 None
d.get(key, default)找不到返回 default
d.pop(key)删除并返回 key 对应的值
d.clear()清空
练习 3 / 7新增 + 修改去闯关页做这题 →
教学 05 / 08

7、Python 3.9+ 的 dict 合并运算符 |

各位童鞋,前面咱们讲了 dict 的增、删、改、查。这里再补一个 Python 3.9 之后才出现的好东西—— dict 合并运算符 | 和 |=。

我们先来看一个需求:水哥手上有两份联系人字典,需要把它们合并成一份。

python到闯关页运行这段 →
old_contacts = {'一点水': '131456780001', '两点水': '131456780002'}
new_contacts = {'三点水': '131456780003', '两点水': '131456780099'}

注意 两点水 这个 key 在两份里都有,我们希望以「新的」为准。

在 Python 3.9 之前,常见的写法是这样:

python到闯关页运行这段 →
merged = {**old_contacts, **new_contacts}

这种 ** 解包写法没毛病,但是不够直观。Python 3.9 之后,可以直接用 |:

python到闯关页运行这段 →
merged = old_contacts | new_contacts
print(merged)

输出的结果:

{'一点水': '131456780001', '两点水': '131456780099', '三点水': '131456780003'}

是不是发现,跟 set 的并集 | 长得一模一样?没错,作者就是这个意思——「把两个 dict 合并起来」。

如果你不想生成新字典,而是直接「在原字典上」合并新内容,那就用 |=:

python到闯关页运行这段 →
old_contacts |= new_contacts
print(old_contacts)

注意:| 是「右边覆盖左边」,跟 {**a, **b} 的逻辑一致。各位用的时候要留意这一点,别把哪边是新的、哪边是旧的搞反了。

顺便:从 Python 3.7 开始,dict 是有序的

从 Python 3.7 开始,dict 保留插入顺序——这是语言级保证:

python到闯关页运行这段 →
d = {}
d['c'] = 3
d['a'] = 1
d['b'] = 2

for key in d:
    print(key)

输出:

c
a
b

遍历的顺序就是当初插入的顺序。所以现在大多数情况下,你不再需要 import collections.OrderedDict,普通的 dict 就够用。

练习 4 / 7用 | 合并两个 dict去闯关页做这题 →
教学 06 / 08

二、set(集合)

Python 的 set 和其他语言类似,是一个无序、不重复元素的集合。基本功能包括关系测试和消除重复元素。

set 和 dict 类似,但是 set 不存 value,只存 key。

1、set 的创建

创建一个 set,传一个 list 进去:

python到闯关页运行这段 →
set1 = set([123, 456, 789])
print(set1)

输出(顺序可能不一样,因为 set 是无序的):

{456, 123, 789}

注意:set 用大括号 {},跟 dict 一样。但是 dict 是 {key: value},set 是 {value, value, value},结构不一样。

⚠️ 空 set 不能写成 {} —— 那会创建一个空 dict。空 set 必须用 set():

python到闯关页运行这段 →
empty_set = set()      # ✓ 空 set
empty_dict = {}        # ✗ 这是空 dict

重复元素自动去重

set 最好用的特性之一:自动去重。

python到闯关页运行这段 →
set1 = set([123, 456, 789, 123, 123])
print(set1)

输出:

{456, 123, 789}

输入有 5 个元素,输出只有 3 个——重复的 123 被自动丢掉了。

不过你也发现了:set 是无序的,打印顺序和放进去的顺序对不上,而且每次可能都不一样。想要一个顺序稳定的结果,可以用内置函数 sorted()——它接收任何可遍历的东西(set、list 都行),返回一个从小到大排好序的新 list:

python到闯关页运行这段 →
set1 = set([123, 456, 789, 123, 123])
print(sorted(set1))

输出:

[123, 456, 789]

「先用 set 去重,再用 sorted 排序」是很常用的组合,马上的练习就会用到。

练习 5 / 7用 set 去重去闯关页做这题 →
教学 07 / 08

2、set 添加元素

通过 add(key) 方法可以添加元素到 set 中。重复添加同一个值不会有效果(因为 set 自动去重)。

python到闯关页运行这段 →
set1 = set([123, 456, 789])
print(set1)

set1.add(100)
print(set1)

set1.add(100)   # 再添加一次 100
print(set1)

输出:

{456, 123, 789}
{456, 123, 100, 789}
{456, 123, 100, 789}

3、set 删除元素

通过 remove(key) 方法可以删除 set 中的元素:

python到闯关页运行这段 →
set1 = set([123, 456, 789])
print(set1)

set1.remove(456)
print(set1)

输出:

{456, 123, 789}
{123, 789}

⚠️ 如果 remove 一个不存在的元素会报错。如果不确定元素是否存在,用 discard —— 找不到也不会报错。

练习 6 / 7set 的 add去闯关页做这题 →
教学 08 / 08

4、set 的集合运算

因为 set 是一个无序不重复元素集,所以两个 set 可以做数学意义上的 并集(union)、交集(intersection)、差集(difference) 等操作。

运算符号含义
交集a & ba 和 b 都有的元素
并集a | ba 和 b 任一有的元素(去重)
差集a - ba 有但 b 没有的元素
对称差a ^ b仅在 a 或仅在 b 中的元素(不要两个都有的)

例子:

python到闯关页运行这段 →
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}

print(a & b)   # 交集
print(a | b)   # 并集
print(a - b)   # 差集
print(a ^ b)   # 对称差

输出(顺序可能不同):

{3, 4}
{1, 2, 3, 4, 5, 6}
{1, 2}
{1, 2, 5, 6}

5、什么时候该用 set 而不是 list?

最后这一点很重要。我们做「成员判断」(x in collection)的时候:

  • 如果 collection 是 list,复杂度是 O(n)——元素越多越慢
  • 如果 collection 是 set(或 dict),复杂度接近 O(1)——跟元素数量基本无关

所以,如果代码里有大量「这个东西在不在那一堆里」的判断,把 list 转成 set 性能会大幅提升:

python到闯关页运行这段 →
names_list = [f'user_{i}' for i in range(100000)]
names_set = set(names_list)

# 这两种结果一样,但是后者快得多
print('user_99999' in names_list)
print('user_99999' in names_set)

set 的价值不光是去重,更重要的是快查。

练习 7 / 7求交集去闯关页做这题 →

读完了?动手练一遍才算真会。

去闯关模式练习 →