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

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),
    ...
]

每个元组包含:

  1. 股票名
  2. 股数
  3. 买入价格

可以通过二维索引访问:

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() 返回当前价格字典

然后计算:

  1. 投资组合原始成本
  2. 当前市场价值
  3. 盈利或亏损

这展示了列表与字典的互补关系:

  • 列表保存多条持仓记录
  • 字典根据股票代码快速查找当前价格

该模式是后续更完整报表程序的基础。

关键收获

  • 列表适合保存有序记录集合。
  • 元组可用于表示固定字段的简单记录。
  • 字典适合通过语义化键访问字段或快速查找数据。
  • 集合适合去重、成员测试和集合运算。
  • 字典键必须是不可变对象,元组可以作为复合键。
  • 真实文件输入可能包含空行或坏数据,程序需要防御性处理。
  • 使用字典列表表示结构化记录通常比元组列表更易读。
  • 股票投资组合示例展示了Python数据结构在实际数据处理中的组合使用。