Files
2026-07-29 14:59:37 +08:00

457 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 第 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`
- [ ] 能回答七道思考题。