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