课程进度 课程大纲 已发布 24/24 课
Python 基础
数据与集合
构建可靠的程序
使用对象建模
专业 Python
高级 Python
列表就像托盘;元组就像密封的卡片
有时,一个值是不够的。你可能需要多个学习主题、七天的每日分数,或一条 (day, minutes) 记录。Python 为有序的数据组提供了两种实用的形式。
- 列表 项目可以移入和移出
- 元组 各个位置组成一条固定记录
- 序列 按顺序读取项目
两者都是序列。其中的项目会保持顺序,从位置 0 开始,并且都可以使用 len()、in、索引和切片。
topics = ["Strings", "Lists", "Dictionaries"]
print(len(topics))
print(topics[0])
print(topics[-1])
print(topics[1:3])
print("Lists" in topics)
3
Strings
Dictionaries
['Lists', 'Dictionaries']
True
切片会在结束位置之前停止,并创建一个新的外层序列。访问一个不存在的索引会引发 IndexError;切片则可以安全地超出末尾。
列表可以增长和缩短
列表使用方括号,并且是可变的:其中的内容可以改变。
queue = ["Strings", "Lists"]
queue.append("Tuples")
queue.extend(["Dictionaries", "Sets"])
queue.insert(1, "Review")
print(queue)
['Strings', 'Review', 'Lists', 'Tuples', 'Dictionaries', 'Sets']
append(x) 添加一个项目。extend(things) 添加 things 中的每一个项目。追加 ["A", "B"] 会创建一个嵌套列表;用它扩展列表则会添加两个字符串。
要按值删除项目,请使用 remove()。要按位置删除项目并保留被删除的值,请使用 pop():
queue = ["Strings", "Review", "Lists"]
queue.remove("Review")
finished = queue.pop(0)
print(finished)
print(queue)
Strings
['Lists']
边界提醒:remove("missing") 会引发 ValueError,而对空列表调用 pop() 会引发 IndexError。如果没有找到项目是正常情况,请先用 in 检查,或者先确认列表不是空的。
两个名称标签可以指向同一个托盘
赋值不会复制列表。它只是在同一个托盘上再放一个名称标签。
-
一个列表
["Strings", "Lists"] - plan 第一个名称标签
- alias 第二个名称标签
- 修改一次 两个名称都能看到变化
alias = plan 共享同一个可变对象。plan = ["Strings", "Lists"]
alias = plan
snapshot = plan.copy()
alias.append("Tuples")
print(plan)
print(snapshot)
print(alias is plan)
print(snapshot is plan)
['Strings', 'Lists', 'Tuples']
['Strings', 'Lists']
True
False
is 问的是“是不是完全同一个对象?”;== 问的是“内容是否相等?”copy() 或 plan[:] 会创建一个新的外层列表。这是浅拷贝,因此嵌套的可变项目仍然会被共享:
week = [["Mon", 20], ["Tue", 30]]
snapshot = week.copy()
week.append(["Wed", 40])
week[0][1] = 99
print(snapshot)
print(week)
[['Mon', 99], ['Tue', 30]]
[['Mon', 99], ['Tue', 30], ['Wed', 40]]
新的星期三托盘只存在于 week 中,但两个外层列表仍然指向同一个星期一内层托盘。
元组会保持一条记录的结构
元组通常使用圆括号。它们是不可变序列:没有 append(),没有 remove(),也不能给其中的项目重新赋值。
- 星期一 位置 0
- 45 分钟 位置 1
- 固定的二元组 这条记录始终包含两个部分
record = ("Monday", 45)
day, minutes = record
print(day)
print(minutes)
Monday
45
除非使用带星号的目标,否则解包时名称的数量必须与项目数量完全相同。只有一个项目的元组需要一个逗号:("Lists",) 是元组,而 ("Lists") 只是字符串。元组本身不能改变,但放在元组内部的可变列表仍然可以改变;元组并不是深度冷冻柜。
小项目:三天学习日志
创建 study_log.py。这个完整程序只使用 Python 内置工具。每天的记录是固定的元组;日志和主题队列则是列表。快照可以证明队列经过了复制,而不是创建了别名。
study_log = [
("Monday", 20),
("Tuesday", 35),
("Wednesday", 35),
]
topic_queue = ["Strings", "Lists", "Dictionaries"]
original_plan = topic_queue.copy()
finished = topic_queue.pop(0)
topic_queue.append("Sets")
total = 0
for day, minutes in study_log:
total += minutes
print(f"{day}: {minutes} minutes")
average = total / len(study_log)
print(f"Total: {total} minutes")
print(f"Average: {average:.1f} minutes")
print(f"Finished: {finished}")
print(f"Original: {original_plan}")
print(f"Now: {topic_queue}")
print(f"Snapshot safe: {original_plan == ['Strings', 'Lists', 'Dictionaries']}")
运行 python3 study_log.py,并检查结尾部分:
Monday: 20 minutes
Tuesday: 35 minutes
Wednesday: 35 minutes
Total: 90 minutes
Average: 30.0 minutes
Finished: Strings
Original: ['Strings', 'Lists', 'Dictionaries']
Now: ['Lists', 'Dictionaries', 'Sets']
Snapshot safe: True
这个程序始终有三条记录,因此进行除法是安全的。如果允许日志为空,那么在做除法之前,你需要先决定“平均值”应该表示什么。
三个小任务
- 追加还是扩展? 对
["Tuples", "Sets"]分别尝试这两种操作。预测哪个结果会出现嵌套。 - 复制侦探。 复制
[["Mon", 10]],然后修改内层的10。解释为什么两个外层列表都能看到这一变化。 - 元组检查员。 创建
(topic, minutes, done),将它解包到三个名称中,然后打印一句话。
当你能做到以下这些时,就可以开始第 7 章了……
- 你可以使用索引和切片读取序列中的项目;
- 你可以在
append()、extend()、remove()和pop()之间做出选择; - 你可以解释为什么别名会共享列表的变化;
- 你可以进行浅拷贝,并说明它在嵌套列表上的边界;
- 你可以在会变化的列表和结构固定的元组之间做出选择;
- 你可以创建只有一个项目的元组,并正确地解包记录;
- 你可以运行学习日志并看到
Snapshot safe: True。
接下来,你将用含义明确的字典键取代数字位置,并在需要“每种只保留一个”时使用集合。