简介
本节讨论列表、字典和集合。
本节讨论列表、字典和集合。
程序常常需要处理许多对象。
有三种主要的选择可供使用。
当数据顺序很重要时,请使用列表。请记住,列表可以容纳任何类型的对象。例如,一个元组列表。
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)
从头开始创建一个列表。
records = [] ## 初始为空列表
## 使用.append() 添加更多项
records.append(('GOOG', 100, 490.10))
records.append(('IBM', 50, 91.3))
...
从文件读取记录时的一个示例。
records = [] ## 初始为空列表
with open('portfolio.csv', 'rt') as f:
next(f) ## 跳过标题行
for line in f:
row = line.split(',')
records.append((row[0], int(row[1]), float(row[2])))
如果你想要快速进行随机查找(按键名),字典会很有用。例如,一个股票价格的字典:
prices = {
'GOOG': 513.25,
'CAT': 87.22,
'IBM': 93.37,
'MSFT': 44.12
}
以下是一些简单的查找操作:
>>> prices['IBM']
93.37
>>> prices['GOOG']
513.25
>>>
从头开始构建字典的示例。
prices = {} ## 初始为空字典
## 插入新项
prices['GOOG'] = 513.25
prices['CAT'] = 87.22
prices['IBM'] = 93.37
从文件内容填充字典的示例。
prices = {} ## 初始为空字典
with open('prices.csv', 'rt') as f:
for line in f:
row = line.split(',')
prices[row[0]] = float(row[1])
注意:如果你在 prices.csv 文件上尝试此操作,你会发现它几乎能正常工作——但文件末尾有一个空行,这会导致程序崩溃。你需要想办法修改代码来处理这个问题(见练习 2.6)。
你可以测试键是否存在。
if key in d:
## 存在
else:
## 不存在
你可以查找一个可能不存在的值,并在其不存在时提供一个默认值。
name = d.get(key, default)
一个示例:
>>> prices.get('IBM', 0.0)
93.37
>>> prices.get('SCOX', 0.0)
0.0
>>>
在 Python 中,几乎任何类型的值都可以用作字典的键。字典的键必须是不可变类型。例如,元组:
holidays = {
(1, 1) : '新年',
(3, 14) : '圆周率日',
(9, 13) : "程序员节",
}
然后进行访问:
>>> holidays[3, 14]
'圆周率日'
>>>
列表、集合或另一个字典都不能用作字典的键,因为列表和字典是可变的。
集合是无序的唯一元素的集合。
tech_stocks = { 'IBM','AAPL','MSFT' }
## 另一种语法
tech_stocks = set(['IBM', 'AAPL', 'MSFT'])
集合对于成员测试很有用。
>>> tech_stocks
set(['AAPL', 'IBM', 'MSFT'])
>>> 'IBM' in tech_stocks
True
>>> 'FB' in tech_stocks
False
>>>
集合对于消除重复项也很有用。
names = ['IBM', 'AAPL', 'GOOG', 'IBM', 'GOOG', 'YHOO']
unique = set(names)
## unique = set(['IBM', 'AAPL','GOOG','YHOO'])
其他集合操作:
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' }
在这些练习中,你将开始构建本课程剩余部分使用的一个主要程序。在 report.py 文件中完成你的工作。
文件 portfolio.csv 包含了一个投资组合中的股票列表。在练习 1.30 中,你编写了一个函数 portfolio_cost(filename),它读取这个文件并进行了一个简单的计算。
你的代码应该类似于以下这样:
## pcost.py
import csv
def portfolio_cost(filename):
'''计算投资组合文件的总成本(股数 * 价格)'''
total_cost = 0.0
with open(filename, 'rt') as f:
rows = csv.reader(f)
headers = next(rows)
for row in rows:
nshares = int(row[1])
price = float(row[2])
total_cost += nshares * price
return total_cost
以这段代码为大致指导,创建一个新文件 report.py。在那个文件中,定义一个函数 read_portfolio(filename),它打开给定的投资组合文件并将其读入一个元组列表中。为此,你需要对上述代码做一些小的修改。
首先,不是定义 total_cost = 0,而是创建一个最初设置为空列表的变量。例如:
portfolio = []
接下来,不是计算成本总和,而是像你在上一个练习中那样将每一行转换为一个元组,并将其追加到这个列表中。例如:
for row in rows:
holding = (row[0], int(row[1]), float(row[2]))
portfolio.append(holding)
最后,返回生成的 portfolio 列表。
通过交互式方式试验你的函数(提醒一下,要这样做,你首先必须在解释器中运行 report.py 程序):
提示:在终端中执行文件时使用 -i
>>> portfolio = read_portfolio('/home/labex/project/portfolio.csv')
>>> portfolio
[('AA', 100, 32.2), ('IBM', 50, 91.1), ('CAT', 150, 83.44), ('MSFT', 200, 51.23),
('GE', 95, 40.37), ('MSFT', 50, 65.1), ('IBM', 100, 70.44)]
>>>
>>> portfolio[0]
('AA', 100, 32.2)
>>> portfolio[1]
('IBM', 50, 91.1)
>>> portfolio[1][1]
50
>>> total = 0.0
>>> for s in portfolio:
total += s[1] * s[2]
>>> print(total)
44671.15
>>>
你创建的这个元组列表与二维数组非常相似。例如,你可以使用类似 portfolio[row][column] 的查找方式来访问特定的行和列,其中 row 和 column 是整数。
也就是说,你也可以使用这样的语句重写最后一个 for 循环:
>>> total = 0.0
>>> for name, shares, price in portfolio:
total += shares*price
>>> print(total)
44671.15
>>>
采用你在练习 2.4 中编写的函数,并进行修改,用字典而非元组来表示投资组合中的每只股票。在这个字典中,使用“name”(名称)、“shares”(股数)和“price”(价格)这些字段名来表示输入文件中的不同列。
按照你在练习 2.4 中的方式对这个新函数进行试验。
>>> portfolio = read_portfolio('/home/labex/project/portfolio.csv')
>>> portfolio
[{'name': 'AA','shares': 100, 'price': 32.2}, {'name': 'IBM','shares': 50, 'price': 91.1},
{'name': 'CAT','shares': 150, 'price': 83.44}, {'name': 'MSFT','shares': 200, 'price': 51.23},
{'name': 'GE','shares': 95, 'price': 40.37}, {'name': 'MSFT','shares': 50, 'price': 65.1},
{'name': 'IBM','shares': 100, 'price': 70.44}]
>>> portfolio[0]
{'name': 'AA','shares': 100, 'price': 32.2}
>>> portfolio[1]
{'name': 'IBM','shares': 50, 'price': 91.1}
>>> portfolio[1]['shares']
50
>>> total = 0.0
>>> for s in portfolio:
total += s['shares']*s['price']
>>> print(total)
44671.15
>>>
在这里,你会注意到每个条目的不同字段是通过键名而非数字列号来访问的。这通常更受青睐,因为这样生成的代码日后更易于阅读。
查看大型字典和列表可能会很杂乱。为了清理输出以便调试,可以考虑使用 pprint 函数。
>>> from pprint import pprint
>>> pprint(portfolio)
[{'name': 'AA', 'price': 32.2,'shares': 100},
{'name': 'IBM', 'price': 91.1,'shares': 50},
{'name': 'CAT', 'price': 83.44,'shares': 150},
{'name': 'MSFT', 'price': 51.23,'shares': 200},
{'name': 'GE', 'price': 40.37,'shares': 95},
{'name': 'MSFT', 'price': 65.1,'shares': 50},
{'name': 'IBM', 'price': 70.44,'shares': 100}]
>>>
字典是一种很有用的方式,可用于跟踪那些你希望使用整数以外的索引来查找条目的情况。在 Python shell 中,尝试操作一个字典:
>>> prices = { }
>>> prices['IBM'] = 92.45
>>> prices['MSFT'] = 45.12
>>> prices
... 查看结果...
>>> prices['IBM']
92.45
>>> prices['AAPL']
... 查看结果...
>>> 'AAPL' in prices
False
>>>
文件 prices.csv 包含一系列股票价格的行。该文件看起来如下所示:
"AA",9.22
"AXP",24.85
"BA",44.85
"BAC",11.27
"C",3.72
...
编写一个函数 read_prices(filename),它将这样一组价格读入一个字典,其中字典的键是股票名称,字典中的值是股票价格。
要做到这一点,从一个空字典开始,然后像上面那样开始向其中插入值。不过,现在你是从文件中读取值。
我们将使用这个数据结构来快速查找给定股票名称的价格。
这部分你需要一些小提示。首先,确保像之前一样使用 csv 模块 —— 这里无需重新发明轮子。
>>> import csv
>>> f = open('/home/labex/project/prices.csv', 'r')
>>> rows = csv.reader(f)
>>> for row in rows:
print(row)
['AA', '9.22']
['AXP', '24.85']
...
[]
>>>
另一个小麻烦是 prices.csv 文件中可能有一些空行。注意上面数据的最后一行是一个空列表 —— 这意味着该行没有数据。
这有可能导致你的程序因异常而崩溃。使用 try 和 except 语句来适当地捕获这种情况。思考一下:用 if 语句来防范错误数据会不会更好呢?
一旦你编写了 read_prices() 函数,通过交互式方式测试它,以确保它能正常工作:
>>> prices = read_prices('/home/labex/project/prices.csv')
>>> prices['IBM']
106.28
>>> prices['MSFT']
20.89
>>>
通过在你的 report.py 程序中添加一些额外的语句来计算收益/损失,将所有这些工作整合在一起。这些语句应该使用练习 2.5 中的股票列表和练习 2.6 中的价格字典,并计算投资组合的当前价值以及收益/损失。
恭喜你!你已经完成了“容器”实验。你可以在 LabEx 中练习更多实验来提升你的技能。