课程进度 课程大纲 已发布 24/24 课
Python 基础
数据与集合
构建可靠的程序
使用对象建模
专业 Python
高级 Python
字典就像贴着标签的抽屉
列表回答“位置 0 上有什么?”字典回答“minutes 抽屉里有什么?”每个抽屉都有一个唯一的键和一个值。
-
键
"topic"是抽屉的名称 - 字典 找到对应的抽屉
-
值
里面装着
"Lists"
session = {
"topic": "Lists",
"minutes": 35,
"completed": True,
}
print(session["topic"])
print(session["minutes"])
Lists
35
在现代 Python 中,字典会保留插入顺序,但它的主要用途是通过键进行查找,而不是通过编号位置查找,也不会自动排序。
打开、添加、更新和移除抽屉
方括号用于读取必需存在的键。赋值操作会添加新键,或者替换已有键所对应的值。
progress = {"Lists": 1, "Tuples": 1}
progress["Dictionaries"] = 1
progress["Lists"] = 2
print(progress)
print("Lists" in progress)
print("Sets" in progress)
{'Lists': 2, 'Tuples': 1, 'Dictionaries': 1}
True
False
对于字典,in 检查的是键,而不是值。使用方括号访问不存在的键会引发 KeyError。如果键可能不存在,可以使用 get() 明确提供默认值:
scores = {"Lists": 0}
print(scores.get("Lists", 0))
print(scores.get("Sets", 0))
print("Lists" in scores)
print("Sets" in scores)
0
0
True
False
第一个 0 是字典中存储的值;第二个表示键不存在。仅靠 get() 无法区分这两种含义。del mapping[key] 会移除一个确定存在的键。mapping.pop(key) 会移除该键并返回它的值。如果键不存在,这两种操作都可能失败,除非你先进行检查,或者有意为 pop() 提供默认值。
需要同时使用键和值时,可以遍历 items():
for topic, count in progress.items():
print(f"{topic}: {count}")
keys() 用于遍历键,values() 用于遍历值。
键的形态必须保持稳定
字典使用哈希快速找到正确的抽屉。键必须是可哈希的:它的哈希值必须保持稳定,而且相等的键必须具有相同的哈希值。
-
字符串
"Lists" -
数字
42 -
固定元组
(3, 5) -
可变列表
[3, 5]
字符串、整数以及仅包含可哈希元素的元组,都是常见的键。列表、字典和集合是可变且不可哈希的,因此不能作为键。包含列表的元组同样不可哈希。字典的值可以是任意 Python 对象。
计数器非常适合使用 get(key, 0),因为键不存在实际上就意味着“出现了零次”:
counts = {}
for topic in ["Lists", "Sets", "Lists"]:
counts[topic] = counts.get(topic, 0) + 1
print(counts)
{'Lists': 2, 'Sets': 1}
集合就像不允许重复贴纸的托盘
一个集合最多只保留一个相等且可哈希的成员。它适合处理唯一性、成员关系以及不同集合之间的关系,而不是维护有意义的显示顺序。
- 重复 两张相同的贴纸到来了
- 集合 只保留一个相等的成员
- 唯一 成员关系保持简单
tags = {"python", "beginner", "python"}
empty_tags = set()
tags.add("collections")
tags.discard("missing")
print(len(tags))
print("python" in tags)
print(type(empty_tags).__name__)
3
True
set
{} 创建的是空字典,因此空集合必须写成 set()。当 x 不存在时,remove(x) 会引发 KeyError;而 discard(x) 会平静地接受“它已经不在了”这一情况。
集合运算可以回答一些很实用的问题:
planned = {"python", "collections", "testing"}
covered = {"python", "collections", "review"}
print((planned | covered) == {"python", "collections", "testing", "review"})
print((planned & covered) == {"python", "collections"})
print((planned - covered) == {"testing"})
print((planned ^ covered) == {"testing", "review"})
print({"python"} <= covered)
这五行都会输出 True。它们分别检查并集、交集、差集、对称差集和子集。应该直接比较集合,绝不要根据显示顺序来判断集合是否相等。如果你既需要唯一性,又需要保留首次出现的顺序,可以用集合记录哪些内容已经出现,再用列表按照顺序显示它们。
小项目:学习主题摘要
创建 topic_summary.py。这个完整程序只使用 Python 的内置集合类型。字典用来描述学习记录和统计主题次数;集合用来移除重复标签;列表用来保留首次出现时的显示顺序。
sessions = [
{"topic": "Lists", "minutes": 35, "tags": ("python", "collections")},
{"topic": "Dictionaries", "minutes": 40, "tags": ("python", "lookup")},
{"topic": "Lists", "minutes": 25, "tags": ("python", "review")},
]
topic_counts = {}
seen_tags = set()
tag_order = []
total = 0
for session in sessions:
topic = session["topic"]
total += session["minutes"]
topic_counts[topic] = topic_counts.get(topic, 0) + 1
for tag in session["tags"]:
if tag not in seen_tags:
seen_tags.add(tag)
tag_order.append(tag)
planned = {"python", "collections", "testing"}
missing = planned - seen_tags
print(f"Total: {total} minutes")
for topic, count in topic_counts.items():
print(f"{topic}: {count} session(s)")
print(f"Tags: {', '.join(tag_order)}")
print(f"Missing planned tags: {len(missing)}")
print(f"Counts correct: {topic_counts == {'Lists': 2, 'Dictionaries': 1}}")
运行 python3 topic_summary.py,并检查输出:
Total: 100 minutes
Lists: 2 session(s)
Dictionaries: 1 session(s)
Tags: python, collections, lookup, review
Missing planned tags: 1
Counts correct: True
边界提醒:这里的每条学习记录都拥有所需的键,并且分钟数都是数字。缺失或类型错误的数据需要进行验证,我们会在第 10 章学习这一内容。
三个小任务
- 寻找零值。 使用
{"Lists": 0},并通过in证明值为零和键不存在是两种不同的情况。 - 单词计数器。 使用字典和
get(word, 0)统计['read', 'code', 'read']。 - 计划比较器。 创建计划主题集合和已完成主题集合;找出两边都有的已完成主题,以及仍未完成的主题。
如果你已经做到以下几点,就可以开始第 8 章了……
- 你可以读取、添加、更新并有意识地移除字典条目;
- 你知道字典中的
in检查的是键; - 你可以区分不存在的键和存储的零值;
- 你可以解释为什么键和集合成员必须是可哈希的;
- 你可以使用
set()创建空集合,并相信它会保留唯一的成员; - 你可以使用五种基本的集合关系,而不依赖顺序;
- 你可以运行主题摘要程序,并看到
Counts correct: True。
接下来,你将使用 enumerate()、zip()、sorted() 和易于阅读的推导式来转换这些集合。