Files
coding-mentor-agent/kb/python-course-kb-practical-python/wiki/summaries/02_Containers.md
T

379 lines
7.7 KiB
Markdown

---
doc_type: short
full_text: sources/02_Containers.md
---
# 02_Containers 总结
本文介绍 Python 中三类核心Python容器:列表、字典和集合,并通过股票投资组合与价格数据的读取练习,展示如何选择合适的数据结构来组织、查询和计算数据。
## 核心主题
程序经常需要处理大量对象,例如:
- 股票投资组合
- 股票价格表
- 多行 CSV 数据记录
Python 常用的三种容器选择是:
| 容器 | 特点 | 典型用途 |
|---|---|---|
| `list` | 有序,可包含任意对象 | 保存顺序重要的记录集合 |
| `dict` | 通过键快速查找值 | 根据名称、编号等键查询数据 |
| `set` | 无序、元素唯一 | 成员测试、去重、集合运算 |
## 列表作为容器
当数据顺序重要时,应使用列表。列表可以保存任意对象,包括元组、字典等复合结构。
示例:用列表保存股票持仓,每条记录是一个元组:
```python
portfolio = [
('GOOG', 100, 490.1),
('IBM', 50, 91.3),
('CAT', 150, 83.44)
]
```
可以通过整数索引访问:
```python
portfolio[0] # ('GOOG', 100, 490.1)
portfolio[2] # ('CAT', 150, 83.44)
```
## 构造列表
列表通常从空列表开始,通过 `.append()` 添加元素:
```python
records = []
records.append(('GOOG', 100, 490.10))
records.append(('IBM', 50, 91.3))
```
从文件读取 CSV 数据时,可以逐行解析并追加到列表中:
```python
records = []
with open('Data/portfolio.csv', 'rt') as f:
next(f) # 跳过表头
for line in f:
row = line.split(',')
records.append((row[0], int(row[1]), float(row[2])))
```
这一模式是后续练习中 `read_portfolio(filename)` 的基础。
## 字典作为容器
字典适合需要根据键进行快速随机查找的场景。例如,用股票代码查找价格:
```python
prices = {
'GOOG': 513.25,
'CAT': 87.22,
'IBM': 93.37,
'MSFT': 44.12
}
```
访问方式:
```python
prices['IBM']
prices['GOOG']
```
与列表通过整数位置访问不同,字典通过具有语义的键访问,常让代码更清晰。
## 构造字典
字典可以从空字典开始构造:
```python
prices = {}
prices['GOOG'] = 513.25
prices['CAT'] = 87.22
prices['IBM'] = 93.37
```
从文件读取价格数据时,可以将股票名作为键、价格作为值:
```python
prices = {}
with open('Data/prices.csv', 'rt') as f:
for line in f:
row = line.split(',')
prices[row[0]] = float(row[1])
```
文中提醒:`Data/prices.csv` 末尾可能存在空行,直接转换可能导致程序崩溃。因此读取真实文件时应处理空行或异常。这引出[[concepts/异常处理]]与数据清洗问题。
## 字典查找
可以用 `in` 判断键是否存在:
```python
if key in d:
# 存在
else:
# 不存在
```
也可以用 `.get()` 提供默认值,避免键不存在时报错:
```python
name = d.get(key, default)
```
示例:
```python
prices.get('IBM', 0.0) # 93.37
prices.get('SCOX', 0.0) # 0.0
```
这在计算投资组合当前市值时很有用,因为某些股票代码可能不在价格字典中。
## 复合键
Python 字典的键必须是不可变对象。元组可以作为字典键,因此适合表达复合索引:
```python
holidays = {
(1, 1): 'New Years',
(3, 14): 'Pi day',
(9, 13): "Programmer's day",
}
```
访问时:
```python
holidays[3, 14]
```
列表、集合和字典不能作为键,因为它们是可变对象。这体现了可变性与不可变性在 Python 数据结构设计中的重要性。
## 集合
集合是无序且元素唯一的容器:
```python
tech_stocks = {'IBM', 'AAPL', 'MSFT'}
```
也可以用 `set()` 构造:
```python
tech_stocks = set(['IBM', 'AAPL', 'MSFT'])
```
集合常用于成员测试:
```python
'IBM' in tech_stocks # True
'FB' in tech_stocks # False
```
也常用于去重:
```python
names = ['IBM', 'AAPL', 'GOOG', 'IBM', 'GOOG', 'YHOO']
unique = set(names)
```
## 集合操作
集合支持常见的数学集合运算:
```python
unique.add('CAT')
unique.remove('YHOO')
s1 = {'a', 'b', 'c'}
s2 = {'c', 'd'}
s1 | s2 # 并集 {'a', 'b', 'c', 'd'}
s1 & s2 # 交集 {'c'}
s1 - s2 # 差集 {'a', 'b'}
```
这些操作适合用于比较不同数据源中的名称、代码或记录集合。
## 练习 2.4:元组列表表示投资组合
练习要求在 `Work/report.py` 中实现:
```python
def read_portfolio(filename):
...
```
该函数读取 `Data/portfolio.csv`,并返回一个由元组组成的列表:
```python
[
('AA', 100, 32.2),
('IBM', 50, 91.1),
...
]
```
每个元组包含:
1. 股票名
2. 股数
3. 买入价格
可以通过二维索引访问:
```python
portfolio[row][column]
```
也可以通过元组解包让代码更清晰:
```python
total = 0.0
for name, shares, price in portfolio:
total += shares * price
```
该练习体现了CSV文件处理、数据建模和序列解包。
## 练习 2.5:字典列表表示投资组合
练习要求将每条股票记录从元组改为字典:
```python
{
'name': 'AA',
'shares': 100,
'price': 32.2
}
```
整体结构变成“字典的列表”:
```python
[
{'name': 'AA', 'shares': 100, 'price': 32.2},
{'name': 'IBM', 'shares': 50, 'price': 91.1},
...
]
```
访问字段时不再依赖数字列号,而是通过字段名:
```python
portfolio[1]['shares']
```
计算总成本:
```python
total = 0.0
for s in portfolio:
total += s['shares'] * s['price']
```
这种结构更可读,也更接近现实中的结构化记录。调试较大的列表或字典时,可以使用:
```python
from pprint import pprint
pprint(portfolio)
```
## 练习 2.6:用字典保存价格表
练习要求实现:
```python
def read_prices(filename):
...
```
该函数读取 `Data/prices.csv`,返回一个价格字典:
```python
{
'IBM': 106.28,
'MSFT': 20.89,
...
}
```
此结构适合根据股票代码快速查询当前价格:
```python
prices['IBM']
prices['MSFT']
```
文中特别强调 `prices.csv` 可能包含空行,使用 `csv.reader()` 时空行会产生空列表:
```python
[]
```
因此实现时需要考虑:
- 用 `if` 判断跳过无效行
- 或用 `try/except` 捕获异常
这部分是对健壮文件读取的入门实践。
## 练习 2.7:计算投资组合盈亏
最后一个练习要求把前面的两个结构结合起来:
- `read_portfolio()` 返回持仓列表
- `read_prices()` 返回当前价格字典
然后计算:
1. 投资组合原始成本
2. 当前市场价值
3. 盈利或亏损
这展示了列表与字典的互补关系:
- 列表保存多条持仓记录
- 字典根据股票代码快速查找当前价格
该模式是后续更完整报表程序的基础。
## 关键收获
- 列表适合保存有序记录集合。
- 元组可用于表示固定字段的简单记录。
- 字典适合通过语义化键访问字段或快速查找数据。
- 集合适合去重、成员测试和集合运算。
- 字典键必须是不可变对象,元组可以作为复合键。
- 真实文件输入可能包含空行或坏数据,程序需要防御性处理。
- 使用字典列表表示结构化记录通常比元组列表更易读。
- 股票投资组合示例展示了Python数据结构在实际数据处理中的组合使用。
## Related Concepts
- [[concepts/集合与集合运算]]
- [[concepts/Python-容器]]
- [[concepts/列表与序列]]
- [[concepts/字典与数据建模]]
- [[concepts/CSV-数据处理]]
- [[concepts/元组与解包]]
- [[concepts/文件读写]]
- [[concepts/上下文管理器]]
- [[concepts/Python-可变对象]]
- [[concepts/Python-不可变对象]]
- [[concepts/字符串处理]]
- [[concepts/函数]]
- [[concepts/模块与-import]]
- [[concepts/Python-交互式解释器]]
- [[concepts/测试-日志与调试]]