feat(python基础): 新增元组教学课程

This commit is contained in:
zhiye.sun
2026-07-29 14:59:37 +08:00
parent 30be6923ed
commit 4181826067
4 changed files with 688 additions and 5 deletions

View File

@@ -0,0 +1,456 @@
# 第 1-9 课:元组
## 一、本课目标
完成本课后,你应该能够:
1. 解释元组与列表的主要区别;
2. 创建空元组、单元素元组和多元素元组;
3. 使用索引、负索引和切片读取元组;
4. 使用 `len()``in``index()``count()`
5. 使用 `for` 遍历元组;
6. 理解元组不可变的含义;
7. 使用元组打包和解包多个数据;
8. 使用星号收集解包后的剩余元素;
9. 完成两个变量的交换;
10. 在列表和元组之间进行转换;
11. 判断什么场景更适合使用元组。
## 二、前置知识
开始本课前,你应该已经掌握:
- 列表的创建、索引和切片;
- 列表的可变性;
- 使用 `for` 遍历多个数据;
- 使用 `in``index()``count()`
- 条件判断与 f-string。
## 三、元组是什么
元组Tuple是一个有顺序的数据集合。
```python
agent_config = ("gpt-5", 0.7, 2000)
```
元组与列表很相似:
- 都能保存多个数据;
- 都有顺序;
- 都支持索引、负索引和切片;
- 都能使用 `len()``for`
最重要的区别是:
- 列表是可变的,可以添加、删除和修改元素;
- 元组是不可变的,创建后不能替换、增加或删除元素。
元组适合表达“创建后不希望被随意改变的一组数据”。
## 四、创建元组
### 4.1 多元素元组
```python
agent_config = ("gpt-5", 0.7, 2000)
```
通常使用英文圆括号 `()` 包围元素,元素之间使用英文逗号分隔。
### 4.2 空元组
```python
empty_tuple = ()
```
使用 `len(empty_tuple)` 会得到 `0`
### 4.3 单元素元组
单元素元组必须在元素后保留英文逗号:
```python
single_tool = ("Python",)
```
如果没有逗号:
```python
not_a_tuple = ("Python")
```
Python 会把圆括号理解为普通分组,变量实际保存的是字符串。
可以使用 `type()` 验证:
```python
print(type(("Python",))) # tuple
print(type(("Python"))) # str
```
创建元组时,真正决定元组的是逗号,而不只是圆括号。
### 4.4 省略圆括号
```python
agent_position = "北京", "朝阳区"
```
只要存在用于分隔元素的逗号Python 也会创建元组。为了新手阅读清晰,本课大多数场景仍保留圆括号。
## 五、元组可以保存不同类型
```python
agent_config = ("gpt-5", 0.7, 2000, True)
```
这个元组依次保存:
- 字符串:模型名称;
- 浮点数:温度参数;
- 整数:最大输出长度;
- 布尔值:是否启用。
虽然允许混合类型,但每个位置应具有明确、稳定的含义。
## 六、索引和负索引
元组索引从 `0` 开始:
```text
元素: gpt-5 0.7 2000
正索引: 0 1 2
负索引: -3 -2 -1
```
```python
agent_config = ("gpt-5", 0.7, 2000)
print(agent_config[0])
print(agent_config[-1])
```
访问不存在的索引会产生 `IndexError`
访问首尾元素前,可以先判断元组是否非空:
```python
if agent_config:
print(agent_config[0])
print(agent_config[-1])
```
## 七、元组切片
切片规则与字符串、列表相同:包含开始索引,不包含结束索引。
```python
agent_config = ("gpt-5", 0.7, 2000, True)
basic_config = agent_config[:2]
remaining_config = agent_config[2:]
reversed_config = agent_config[::-1]
```
元组切片返回一个新元组,不会修改原元组。
## 八、长度、成员判断和统计
### 8.1 `len()`
```python
config_count = len(agent_config)
```
### 8.2 `in`
```python
contains_model = "gpt-5" in agent_config
```
### 8.3 `index()`
```python
if "gpt-5" in agent_config:
model_index = agent_config.index("gpt-5")
```
元素不存在时,`index()` 会产生 `ValueError`,因此可以先使用 `in` 判断。
### 8.4 `count()`
```python
status_history = ("成功", "失败", "成功")
success_count = status_history.count("成功")
```
## 九、遍历元组
```python
supported_models = ("gpt-5", "o3", "gpt-4.1")
for model in supported_models:
print(model)
```
需要编号时可以使用 `enumerate()`
```python
for number, model in enumerate(supported_models, start=1):
print(f"{number}. {model}")
```
## 十、元组不可变
下面的操作都不允许:
```text
agent_config[0] = "o3"
agent_config.append("新数据")
del agent_config[0]
```
元组没有 `append()``remove()``pop()` 等修改方法。
不可变带来的好处包括:
- 表达数据结构是固定的;
- 减少意外修改;
- 阅读代码时更容易判断数据不会被增删;
- 某些场景中元组可以作为字典的键,后续学习字典时会再讲。
## 十一、需要“修改”元组怎么办
不能直接修改原元组,但可以创建新元组:
```python
old_config = ("gpt-5", 0.7, 2000)
new_config = ("o3",) + old_config[1:]
print(old_config)
print(new_config)
```
原元组保持不变,`new_config` 是全新的元组。
也可以先转换为列表,修改后再转回元组:
```python
config_list = list(old_config)
config_list[0] = "o3"
new_config = tuple(config_list)
```
如果数据经常需要修改,直接使用列表通常更合适,不应反复转换。
## 十二、元组拼接和重复
虽然不能修改原元组,但可以通过运算创建新元组。
### 12.1 拼接
```python
basic_config = ("gpt-5", 0.7)
limit_config = (2000, True)
full_config = basic_config + limit_config
```
### 12.2 重复
```python
default_status = ("待处理",) * 3
```
结果为:
```text
('待处理', '待处理', '待处理')
```
这两种操作都返回新元组。
## 十三、元组打包
把多个数据放进一个元组称为打包Packing
```python
agent_snapshot = "gpt-5", 0.7, 2000
```
三个数据被打包成一个元组。
使用圆括号可以让含义更清楚:
```python
agent_snapshot = ("gpt-5", 0.7, 2000)
```
## 十四、元组解包
把元组中的元素分别赋给多个变量称为解包Unpacking
```python
agent_snapshot = ("gpt-5", 0.7, 2000)
model_name, temperature, max_tokens = agent_snapshot
```
解包后:
- `model_name` 保存 `"gpt-5"`
- `temperature` 保存 `0.7`
- `max_tokens` 保存 `2000`
左侧变量数量必须与右侧元素数量一致,否则会产生 `ValueError`
## 十五、使用星号收集剩余元素
```python
agent_snapshot = ("gpt-5", 0.7, 2000, True)
model_name, *other_settings = agent_snapshot
```
解包后:
- `model_name` 是字符串 `"gpt-5"`
- `other_settings` 是列表 `[0.7, 2000, True]`
星号变量会把剩余元素收集到列表中。
一个解包表达式中只能有一个星号变量:
```python
first_value, *middle_values, last_value = agent_snapshot
```
## 十六、交换变量
传统思路通常需要临时变量。Python 可以使用打包和解包直接交换:
```python
primary_model = "gpt-5"
backup_model = "o3"
primary_model, backup_model = backup_model, primary_model
```
右侧先打包为元组,再解包给左侧变量。
## 十七、元组内部包含可变对象
请观察:
```python
agent_data = ("gpt-5", ["搜索", "计算"])
agent_data[1].append("写作")
```
元组本身仍然不可变:不能把 `agent_data[1]` 替换成另一个列表。
但是第二个元素指向的列表本身是可变对象,因此列表内部可以改变。
这说明元组不可变的准确含义是:元组保存的元素引用不能被替换,并不保证元素内部的数据一定不可变。
这是一个重要边界。本课能看懂即可,后续学习对象和引用时会继续加深理解。
## 十八、列表与元组如何选择
适合使用列表:
- 数据需要经常添加、删除、修改;
- 数量会变化;
- 表达任务清单、工具集合、学习记录。
适合使用元组:
- 数据结构固定;
- 创建后不希望改变;
- 表达坐标、颜色值、固定配置、数据库返回的一条固定结构记录;
- 需要通过解包一次取得多个数据。
不要因为元组“看起来更高级”而强行使用。应该根据数据是否需要变化来选择。
## 十九、运行本课示例
在 PowerShell 中执行:
```powershell
cd D:\Code\Python\01_python基础\1_9_元组
python tuples.py
```
示例无需用户输入,会依次展示创建、读取、切片、统计、遍历、拼接、转换、打包、解包和变量交换。
## 二十、常见错误
### 20.1 单元素元组忘记逗号
```python
single_value = ("Python",)
```
最后的逗号不可省略。
### 20.2 尝试直接修改元组
元组不支持按索引赋值,也没有列表的增删方法。
### 20.3 解包变量数量不匹配
三个元素通常需要三个接收变量,除非使用星号收集剩余元素。
### 20.4 认为圆括号一定代表元组
`("Python")` 是字符串;逗号才是创建单元素元组的关键。
### 20.5 把元组中的列表也当作完全不可变
元组不能替换元素,但元组内部的列表仍然可以修改自身内容。
## 二十一、课堂练习
打开 `practice.py`完成“Agent 固定配置快照”。
运行命令:
```powershell
python practice.py
```
练习会要求你创建配置元组、读取和切片、查找统计、解包、转换以及比较原配置和新配置。
## 二十二、思考题
1. 列表和元组最重要的区别是什么?
2. 为什么单元素元组必须保留逗号?
3. 元组切片会返回列表还是元组?
4. 解包时变量数量不匹配会发生什么?
5. 星号变量收集剩余元素后得到什么类型?
6. 为什么经常变化的数据更适合使用列表?
7. 元组中包含列表时,为什么列表内容仍然能够变化?
## 二十三、本课小结
- 元组是有顺序、不可变的数据集合;
- 单元素元组必须包含逗号;
- 元组支持索引、切片、遍历、成员判断和统计;
- 元组不能直接增删或替换元素;
- 需要新数据时,可以创建新元组或进行列表转换;
- 打包把多个数据组合成元组;
- 解包把元组元素分别赋给变量;
- 星号可以收集剩余元素;
- Python 可以通过打包解包交换变量;
- 固定结构适合元组,经常变化的数据适合列表。
## 二十四、验收标准
- [ ] 能创建空元组、单元素元组和多元素元组;
- [ ] 能使用索引和切片读取元组;
- [ ] 能使用 `len()``in``index()``count()`
- [ ] 能遍历元组;
- [ ] 能解释元组不可变;
- [ ] 能完成打包和解包;
- [ ] 能使用星号收集剩余元素;
- [ ] 能在列表和元组之间转换;
- [ ] 能完成并运行 `practice.py`
- [ ] 能回答七道思考题。