目录 | 上一节 (2.1 数据类型) | 下一节 (2.3 格式化)

2.2 容器

本节讨论列表(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)

列表构建

从零开始构建列表。

records = []  # Initial empty list

# Use .append() to add more items
records.append(('GOOG', 100, 490.10))
records.append(('IBM', 50, 91.3))
...

从文件读取记录的示例:

records = []  # Initial empty list

with open('Data/portfolio.csv', 'rt') as f:
next(f) # Skip header
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 = {} # Initial empty dict

# Insert new items
prices['GOOG'] = 513.25
prices['CAT'] = 87.22
prices['IBM'] = 93.37

从文件内容填充字典的示例:

prices = {} # Initial empty dict

with open('Data/prices.csv', 'rt') as f:
for line in f:
row = line.split(',')
prices[row[0]] = float(row[1])

注意:如果是在 Data/prices.csv 文件上尝试此操作,会发现几乎可以正常工作——但是,在末尾有一个空行导致程序崩溃了。需要找出一些方法来修改代码以解决此问题(参见练习 2.6)。

字典查找

测试键是否存在:

if key in d:
# YES
else:
# NO

可以查找可能不存在的值,并在值不存在的情况下提供默认值。

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]
'Pi day'
>>>

列表,集合或者其它字典都不能用作字典的键,因为列表和字典(译注:集合也是使用哈希技术实现的)是可变的。

集合

集合是互异且无序的数据。

tech_stocks = { 'IBM','AAPL','MSFT' }
# Alternative syntax
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'])

其它集合操作:

names.add('CAT')        # Add an item
names.remove('YHOO') # Remove an item s1 | s2 # Set union
s1 & s2 # Set intersection
s1 - s2 # Set difference

练习

在这些练习中,你开始构建的程序是本课程剩余部分使用的主要程序之一。请在 Work/report.py 文件中工作。

练习 2.4:包含元组的列表

Data/portfolio.csv 文件包含投资组合中的股票列表。在 练习 1.30 中,你编写了一个读取该文件并执行简单计算的 portfolio_cost(filename) 函数。

代码看起来应该像下面这样:

# pcost.py

import csv

def portfolio_cost(filename):
'''Computes the total cost (shares*price) of a portfolio file'''
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 。在 report.py 文件中,定义 read_portfolio(filename) 函数,该函数打开 Data/portfolio.csv 文件并将其读入到包含元组的列表中。为此,你需要对上面的代码做一些小修改。

首先,创建一个最初设为空列表的变量,而不是定义 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('Data/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
>>>

创建的包含元组的列表非常类似于二维(2-D)数组。例如,使用诸如 portfolio[row][column]rowcolumn 是整数)的查找来访问特定的列和行。

也就是说,可以使用像下面这样的语句重写最后的 for 循环:

>>> total = 0.0
>>> for name, shares, price in portfolio:
total += shares*price >>> print(total)
44671.15
>>>

练习 2.5:包含字典的列表

使用字典(而不是元组)修改在练习 2.4 中编写的函数来表示投资组合中的股票。在字典中,使用字段名 "name", "shares" 和 "price" 来表示输入文件中的不同列。

以与练习 2.4 中相同的方式试验这个新的函数。

>>> portfolio = read_portfolio('Data/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}]
>>>

练习 2.6:把字典当作容器

在使用索引而不是数字查找某元素的地方,字典是一种用来跟踪元素的很有用的方式。在 Python shell 中,尝试使用字典:

>>> prices = { }
>>> prices['IBM'] = 92.45
>>> prices['MSFT'] = 45.12
>>> prices
... look at the result ...
>>> prices['IBM']
92.45
>>> prices['AAPL']
... look at the result ...
>>> 'AAPL' in prices
False
>>>

Data/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('Data/prices.csv', 'r')
>>> rows = csv.reader(f)
>>> for row in rows:
print(row) ['AA', '9.22']
['AXP', '24.85']
...
[]
>>>

另外一个小麻烦是 Data/prices.csv 文件可能有一些空行在里面。注意上面数据的最后一行是一个空列表——意味在那一行没有数据。

这有可能导致你的程序因为异常而终止。酌情使用 tryexcept 语句捕获这些异常。思考:使用 if 语句来防范错误的数据是否会更好?

编写完 read_prices() 函数,请交互式地测试它并确保其正常工作:

>>> prices = read_prices('Data/prices.csv')
>>> prices['IBM']
106.28
>>> prices['MSFT']
20.89
>>>

练习 2.7:看看你是否可以退休

通过添加一些计算盈亏的语句到 report.py 程序,将所有的工作联系到一起。这些语句应该采用在练习 2.5 中存储股票名称的列表,以及在练习 2.6 中存储股票价格的字典,并计算投资组合的当前值以及盈亏。

目录 | 上一节 (2.1 数据类型) | 下一节 (2.3 格式化)

注:完整翻译见 https://github.com/codists/practical-python-zh

翻译:《实用的Python编程》02_02_Containers的更多相关文章

  1. 翻译:《实用的Python编程》InstructorNotes

    实用的 Python 编程--讲师说明 作者:戴维·比兹利(David Beazley) 概述 对于如何使用我的课程"实用的 Python 编程"进行教学的问题,本文档提供一些通用 ...

  2. 翻译:《实用的Python编程》README

    欢迎光临 大约 25 年前,当我第一次学习 Python 时,发现 Python 竟然可以被高效地应用到各种混乱的工作项目上,我立即被震惊了.15 年前,我自己也将这种乐趣教授给别人.教学的结果就是本 ...

  3. 翻译:《实用的Python编程》05_02_Classes_encapsulation

    目录 | 上一节 (5.1 再谈字典) | 下一节 (6 生成器) 5.2 类和封装 创建类时,通常会尝试将类的内部细节进行封装.本节介绍 Python 编程中有关封装的习惯用法(包括私有变量和私有属 ...

  4. 翻译:《实用的Python编程》04_02_Inheritance

    目录 | 上一节 (4.1 类) | 下一节 (4.3 特殊方法) 4.2 继承 继承(inheritance)是编写可扩展程序程序的常用手段.本节对继承的思想(idea)进行探讨. 简介 继承用于特 ...

  5. 翻译:《实用的Python编程》01_02_Hello_world

    目录 | 上一节 (1.1 Python) | 下一节 (1.3 数字) 1.2 第一个程序 本节讨论有关如何创建一个程序.运行解释器和调试的基础知识. 运行 Python Python 程序始终在解 ...

  6. 翻译:《实用的Python编程》03_03_Error_checking

    目录 | 上一节 (3.2 深入函数) | 下一节 (3.4 模块) 3.3 错误检查 虽然前面已经介绍了异常,但本节补充一些有关错误检查和异常处理的其它细节. 程序是如何运行失败的 Python 不 ...

  7. 翻译:《实用的Python编程》03_04_Modules

    目录 | 上一节 (3.3 错误检查) | 下一节 (3.5 主模块) 3.4 模块 本节介绍模块的概念以及如何使用跨多个文件的函数. 模块和导入 任何一个 Python 源文件都是一个模块. # f ...

  8. 翻译:《实用的Python编程》03_05_Main_module

    目录 | 上一节 (3.4 模块) | 下一节 (3.6 设计讨论) 3.5 主模块 本节介绍主程序(主模块)的概念 主函数 在许多编程语言中,存在一个主函数或者主方法的概念. // c / c++ ...

  9. 翻译:《实用的Python编程》04_01_Class

    目录 | 上一节 (3.6 设计讨论) | 下一节 (4.2 继承) 4.1 类 本节介绍 class 语句以及创建新对象的方式. 面向对象编程(OOP) 面向对象编程是一种将代码组织成对象集合的编程 ...

随机推荐

  1. Docker安装mysql5.7并且配置主从复制

    Docker安装mysql5.7并且配置主从复制 一.拉取mysql镜像 二.创建文件docker.cnf 2.1 mysql主机(192.168.21.55:3307) 2.1.1 创建文件夹 2. ...

  2. Django(视图)

    一个视图函数,简称视图,是一个简单的Python 函数,它接受Web请求并且返回Web响应.响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片. . . 是任何 ...

  3. Java 复习整理day10

    package com.it.demo01_quickstart; /* 案例: 讲解网络编程相关概念. 网络编程简介: 概述: 网络编程也叫: 套接字编程, Socket编程, 就是用来实现 网络互 ...

  4. Flink-v1.12官方网站翻译-P003-Real Time Reporting with the Table API

    利用表格API进行实时报告 Apache Flink提供的Table API是一个统一的.关系型的API,用于批处理和流处理,即在无边界的.实时的流或有边界的.批处理的数据集上以相同的语义执行查询,并 ...

  5. 加快你ROS安装的一篇文章

    前言: 首先ROS大家应该比较熟悉了哈,如果需要补充一下请看我之前的这篇文章 <嵌入式的我们为什么要学ROS>,对于嵌入式来说ROS是一个很好的进阶方向,所以如何快速的安装一个ROS到我们 ...

  6. A - A Supermarket (贪心, 并查集)

    超市里有n个产品要卖,每个产品都有一个截至时间dx(从开始卖时算起),只有在这个截至时间之前才能卖出并且获得率润dy. 有多个产品,所有可以有不同的卖出顺序,每卖一个产品要占用1个单位的时间,问最多能 ...

  7. gym101002K. Inversions (FFT)

    题意:给定一个仅含有AB的字母串 如果i有一个B j有一个A 且j>i 会对F(j-i)产生贡献 求出所有发Fi 题解:好像是很裸的FFT B的分布可以看作一个多项式 同理A也可以 然后把B的位 ...

  8. codeforces626D . Jerry's Protest (概率)

    Andrew and Jerry are playing a game with Harry as the scorekeeper. The game consists of three rounds ...

  9. UVALive 7276 Wooden Signs

    详细题目见:http://7xjob4.com1.z0.glb.clouddn.com/0f10204481da21e62f8c145939e5828e 思路:记dp[i][j]表示第i个木板尾部在j ...

  10. Codeforces Round #686 (Div. 3) E. Number of Simple Paths (思维,图,bfs)

    题意:有一个\(n\)个点,\(n\)条边的图,问你长度至少为\(1\)的简单路径有多少条. 题解:根据树的性质,我们知道这颗树一定存在一个环,假如一棵树没有环,那么它的所有长度不小于\(1\)的简单 ...