### 6.2.2 列表
我们先回顾第 2 章中介绍的关于列表的知识。 列表是由多个数据组成的序列,可以通过索引(位置序号)来访问列表中的数据。与很
多编程语言提供的数组(array)类型不同,Python 列表具有两个特点:第一,列表成员可 以由任意类型的数据构成,不要求各成员具有相同类型;第二,列表长度是不定的,随时可 以增加和删除成员。另外,与 Python 字符串类型不同,Python 列表是可以修改的,修改方 式包括向列表添加成员、从列表删除成员以及对列表的某个成员进行修改。
作为序列的一种,我们可以对列表施加序列的基本操作,如索引、合并和复制等(参见 表 6.1)。另外由于列表是可以修改的,Python 还为列表提供了修改操作,见表 6.3。
| 修改方式 | 含义 |
| --- | --- |
| a[i] = x | 将列表 a 中索引为 i 的成员改为 x |
| a[i:j] = b | 将列表 a 中索引从 i 到 j(不含)的片段改为列表 b |
| del a[i] | 将列表 a 中索引为 i 的成员删除 |
| del a[i:j] | 将列表 a 中索引从 i 到 j(不含)的片段删除 |
表 6.3 列表的修改
本节要引入的是面向对象方式的列表操作。和字符串一样,Python 列表实际上也是对 象,提供了很多有用的方法。例如,append()方法用于向列表尾部添加成员数据:
```
>>> a = ['hi']
>>> a.append('there')
>>> a
['hi', 'there']
```
利用 append()方法,我们可以将用户输入的一批数据存储到一个列表中:
```
data = []
x = raw_input('Enter a number: ')
while x != "":
data.append(eval(x))
x = raw_input("Enter a number: ")
```
这段代码实际上是累积算法,其中的列表 data 就是累积器:首先初始化为空列表,然后通 过循环来逐步累积(添加成员数据)。
表 6.4 列出了列表对象的常用方法。
| 方法 | 含义 |
| --- | --- |
| <列表>.append(x) | 将 x 添加到<列表>的尾部 |
| <列表>.sort() | 对<列表>排序(使用缺省比较函数 cmp) |
| <列表>.sort(mycmp) | 对<列表>排序(使用自定义比较函数 mycmp) |
| <列表>.reverse() | 将<列表>次序颠倒 |
| <列表>.index(x) | 返回 x 在<列表>中第一次出现处的索引 |
| <列表>.insert(i,x) | 在<列表>中索引 i 处插入成员 x |
| <列表>.count(x) | 返回<列表>中 x 的出现次数 |
| <列表>.remove(x) | 删除<列表>中 x 的第一次出现 |
| <列表>.pop() | 删除<列表>中最后一个成员并返回该成员 |
| <列表>.pop(i) | 删除<列表>中第 i 个成员并返回该成员 |
表 6.4 列表对象的方法
下面通过例子来说明对列表对象的处理:
```
>>> a = ['Irrational',[3.14,2.718],'pi and e']
>>> a.sort()
>>> a
[[3.14, 2.718], 'Irrational', 'pi and e']
>>> a[0].reverse()
>>> a
[[2.718, 3.14], 'Irrational', 'pi and e']
>>> a.insert(2,'number')
>>> a
[[2.718, 3.14], 'Irrational', 'number', 'pi and e']
>>> print a.pop(0)
[2.718, 3.14]
>>> a
['Irrational', 'number', 'pi and e']
```
编程案例:一个统计程序 对大量数据进行统计、分析是实际应用中常见的问题,通过计算一些统计指标可以获得有关这批数据的多侧面的特征。常用的统计指标包括总和、算术平均值、中位数、众数、标 准差和方差等,这些指标的计算过程具有不同的特性。
“总和”是可以累积计算的,即可以先计算部分数据的和 sum,当有了新数据再加入 sum 并形成新的 sum。重复上述步骤直至所有数据都已加入 sum,这时所得即总和。利用我 们介绍过的累积算法模式,很容易实现求总和的代码:
```
sum = 0
data = raw_input("输入新数据: ")
while data != "":
x = eval(data)
sum = sum + x
```
从以上代码可以看到,虽然用户输入了很多数据,但程序中却始终只用一个简单变量data 来存储输入的数据。为什么不怕后面输入的数据将前面输入的数据覆盖掉呢?巧妙之处 在于,累积算法每次接收一个输入数据就立即使用该数据(将新数据加到累加变量 sum 中), 从而使变量 data 可以用于存储下一个输入数据。我们没有采用“先将所有输入数据存储起 来,然后再求和”的处理策略,因为这个策略需要大量存储空间,更麻烦的是我们预先并不 知道需要多少存储空间。类似地,输入数据的“个数”也可以利用累积算法来求得。
再看“算术平均值”指标,虽然它本身不能直接累积计算,但根据公式“平均值=总和÷数据个数”可见,可以通过累积算法求得“总和”和“数据个数”,然后直接算出平均值。 推而广之,如果某个统计指标可以表示成某些累积类型指标的代数式,那么这个指标就可以 利用累积算法进行计算,无需保存所有输入数据。
再看一个统计指标——中位数(median)。中位数将全体数据划分为小于和大于它的两 部分,并且两部分的数据个数相等。如果全体数据从小到大有序排列,则处于中间位置的那 个数据就是中位数①。例如,数据集合{3,4,22,50,64}的中位数是 22。中位数的计算与总和、 算术平均值都不同,因为它不能通过累积来计算,如{3,4}的中位数与{3,4,22}的中位数直至{3,4,22,50,64}的中位数基本没什么关系。因此,为了对用户输入的一组数据求中位数,必须 将每个数据先保存起来,等全体数据都到位后才能计算。与中位数类似的、不具有累积计算 性质的统计指标还有众数、标准差等,可以称之为“整体型”指标,即它们都需要针对全体 数据进行计算。那么,如何存储所有输入数据呢?显然,定义许多独立变量来存储输入数据 是不合适的,因为我们不知道用户会输入多少个数据;即使知道用户将输入 n 个数据,定义 n 个独立变量来存储这些数据也是非常笨拙的做法。其实问题很容易解决,列表可以将所有 输入数据组合成单个数据,这样既保存了所有数据,又不需要定义许多独立变量。
下面我们来编写一个统计程序,其功能是获得用户输入的数值数据,并求出这批数据的 总和、算术平均值和中位数。如前所述,这三个指标分别代表三种类型的统计指标,因此我 们的统计程序虽然简单,但具有一般的意义。
按照模块化设计思想,我们分别为数据输入及每个指标的计算设计一个函数。 首先设计获得输入数据的函数。由于整体型指标中位数的计算需要用到全体输入数据,因此我们先将所有输入数据存储到一个列表中。获得用户输入的关键代码是一个哨兵循环, 数据列表是一个累积器,在循环中逐个接收数据。代码如下:
> ① 若数据个数为偶数,则取处于中间位置的两个数据的平均值。
```
def getInput(): data = []
x = raw_input("Enter a number (<Enter> to quit): ")
while x != "":
data.append(eval(x))
x = raw_input("Enter a number (<Enter> to quit): ")
return data
```
接着设计三个统计指标的函数。这些函数的参数都是列表 aList,调用时将存储输入数 据的 data 作为实参传递给 aList 即可。总和及算术平均值很容易计算,只要先对输入列表利 用累积求得总和,然后再除以列表长度即得平均值。列表长度可以用 len()直接求得,不需 要另外写一个累积循环。代码如下:
```
def sum(aList):
s = 0.0
for x in aList:
s = s + x
return s
def mean(aList):
return sum(aList) / len(aList)
```
中位数的计算没有代数公式可用,我们先将全体数据从小到大排序,然后取中间位置的 数据值。当数据个数为奇数时,有唯一的中间位置,故中位数很容易找到;当数据个数为偶 数时,中位数是处于中间位置的两个数据的平均值。列表数据的排序可以利用现成的列表对 象方法 sort()实现,而奇偶性可以利用余数运算的结果来判断。代码如下:
```
def median(aList):
aList.sort()
size = len(aList)
mid = size / 2
if size % 2 == 1:
m = aList[mid]
else:
m = (aList[mid] + aList[mid-1]) / 2.0
return m
```
利用以上四个模块,再加上主控模块 main(),就完成了我们的统计程序。完整代码见程 序 6.1。
【程序 6.1】statistics.py
```
def getInputs(): d = []
x = raw_input("Enter a number (<Enter> to quit): ")
while x != "":
d.append(eval(x))
x = raw_input("Enter a number (<Enter> to quit): ")
return d
def sum(aList): s = 0.0
for x in aList:
s = s + x
return s
def mean(aList):
return sum(aList) / len(aList)
def median(aList): aList.sort()
size = len(aList)
mid = size / 2
if size % 2 == 1:
m = aList[mid]
else:
m = (aList[mid] + aList[mid-1]) / 2.0
return m
def main():
print "This program computes sum, mean and median."
data = getInputs()
sigma = sum(data)
xbar = mean(data)
med = median(data)
print "Sum:", sigma
print "Average:", xbar
print "Median:", med
main()
```
- 前言
- 第 1 章 计算与计算思维
- 1.1 什么是计算?
- 1.1.1 计算机与计算
- 1.1.2 计算机语言
- 1.1.3 算法
- 1.1.4 实现
- 1.2 什么是计算思维?
- 1.2.1 计算思维的基本原则
- 1.2.2 计算思维的具体例子
- 1.2.3 日常生活中的计算思维
- 1.2.4 计算思维对其他学科的影响
- 1.3 初识 Python
- 1.3.1 Python 简介
- 1.3.2 第一个程序
- 1.3.3 程序的执行方式
- 1.3.4 Python 语言的基本成分
- 1.4 程序排错
- 1.5 练习
- 第 2 章 用数据表示现实世界
- 2.1 数据和数据类型
- 2.1.1 数据是对现实的抽象
- 2.1.1 常量与变量
- 2.1.2 数据类型
- 2.1.3 Python 的动态类型*
- 2.2 数值类型
- 2.2.1 整数类型 int
- 2.2.2 长整数类型 long
- 2.2.3 浮点数类型 float
- 2.2.4 数学库模块 math
- 2.2.5 复数类型 complex*
- 2.3 字符串类型 str
- 2.3.1 字符串类型的字面值形式
- 2.3.2 字符串类型的操作
- 2.3.3 字符的机内表示
- 2.3.4 字符串类型与其他类型的转换
- 2.3.5 字符串库 string
- 2.4 布尔类型 bool
- 2.4.1 关系运算
- 2.4.2 逻辑运算
- 2.4.3 布尔代数运算定律*
- 2.4.4 Python 中真假的表示与计算*
- 2.5 列表和元组类型
- 2.5.1 列表类型 list
- 2.5.2 元组类型 tuple
- 2.6 数据的输入和输出
- 2.6.1 数据的输入
- 2.6.2 数据的输出
- 2.6.3 格式化输出
- 2.7 编程案例:查找问题
- 2.8 练习
- 第 3 章 数据处理的流程控制
- 3.1 顺序控制结构
- 3.2 分支控制结构
- 3.2.1 单分支结构
- 3.2.2 两路分支结构
- 3.2.3 多路分支结构
- 3.3 异常处理
- 3.3.1 传统的错误检测方法
- 3.3.2 传统错误检测方法的缺点
- 3.3.3 异常处理机制
- 3.4 循环控制结构
- 3.4.1 for 循环
- 3.4.2 while 循环
- 3.4.3 循环的非正常中断
- 3.4.4 嵌套循环
- 3.5 结构化程序设计
- 3.5.1 程序开发过程
- 3.5.2 结构化程序设计的基本内容
- 3.6 编程案例:如何求 n 个数据的最大值?
- 3.6.1 几种解题策略
- 3.6.2 经验总结
- 3.7 Python 布尔表达式用作控制结构*
- 3.8 练习
- 第 4 章 模块化编程
- 4.1 模块化编程基本概念
- 4.1.1 模块化设计概述
- 4.1.2 模块化编程
- 4.1.3 编程语言对模块化编程的支持
- 4.2 Python 语言中的函数
- 4.2.1 用函数减少重复代码 首先看一个简单的用字符画一棵树的程序:
- 4.2.2 用函数改善程序结构
- 4.2.3 用函数增强程序的通用性
- 4.2.4 小结:函数的定义与调用
- 4.2.5 变量的作用域
- 4.2.6 函数的返回值
- 4.3 自顶向下设计
- 4.3.1 顶层设计
- 4.3.2 第二层设计
- 4.3.3 第三层设计
- 4.3.4 第四层设计
- 4.3.5 自底向上实现与单元测试
- 4.3.6 开发过程小结
- 4.4 Python 模块*
- 4.4.1 模块的创建和使用
- 4.4.2 Python 程序架构
- 4.4.3 标准库模块
- 4.4.4 模块的有条件执行
- 4.5 练习
- 第 5 章 图形编程
- 5.1 概述
- 5.1.1 计算可视化
- 5.1.2 图形是复杂数据
- 5.1.3 用对象表示复杂数据
- 5.2 Tkinter 图形编程
- 5.2.1 导入模块及创建根窗口
- 5.2.2 创建画布
- 5.2.3 在画布上绘图
- 5.2.4 图形的事件处理
- 5.3 编程案例
- 5.3.1 统计图表
- 5.3.2 计算机动画
- 5.4 软件的层次化设计:一个案例
- 5.4.1 层次化体系结构
- 5.4.2 案例:图形库 graphics
- 5.4.3 graphics 与面向对象
- 5.5 练习
- 第 6 章 大量数据的表示和处理
- 6.1 概述
- 6.2 有序的数据集合体
- 6.2.1 字符串
- 6.2.2 列表
- 6.2.3 元组
- 6.3 无序的数据集合体
- 6.3.1 集合
- 6.3.2 字典
- 6.4 文件
- 6.4.1 文件的基本概念
- 6.4.2 文件操作
- 6.4.3 编程案例:文本文件分析
- 6.4.4 缓冲
- 6.4.5 二进制文件与随机存取*
- 6.5 几种高级数据结构*
- 6.5.1 链表
- 6.5.2 堆栈
- 6.5.3 队列
- 6.6 练习
- 第 7 章 面向对象思想与编程
- 7.1 数据与操作:两种观点
- 7.1.1 面向过程观点
- 7.1.2 面向对象观点
- 7.1.3 类是类型概念的发展
- 7.2 面向对象编程
- 7.2.1 类的定义
- 7.2.2 对象的创建
- 7.2.3 对象方法的调用
- 7.2.4 编程实例:模拟炮弹飞行
- 7.2.5 类与模块化
- 7.2.6 对象的集合体
- 7.3 超类与子类*
- 7.3.1 继承
- 7.3.2 覆写
- 7.3.3 多态性
- 7.4 面向对象设计*
- 7.5 练习
- 第 8 章 图形用户界面
- 8.1 图形用户界面概述
- 8.1.1 程序的用户界面
- 8.1.2 图形界面的组成
- 8.1.3 事件驱动
- 8.2 GUI 编程
- 8.2.1 UI 编程概述
- 8.2.2 初识 Tkinter
- 8.2.3 常见 GUI 构件的用法
- 8.2.4 布局
- 8.2.5 对话框*
- 8.3 Tkinter 事件驱动编程
- 8.3.1 事件和事件对象
- 8.3.2 事件处理
- 8.4 模型-视图设计方法
- 8.4.1 将 GUI 应用程序封装成对象
- 8.4.2 模型与视图
- 8.4.3 编程案例:汇率换算器
- 8.5 练习
- 第 9 章 模拟与并发
- 9.1 模拟
- 9.1.1 计算机建模
- 9.1.2 随机问题的建模与模拟
- 9.1.3 编程案例:乒乓球比赛模拟
- 9.2 原型法
- 9.3 并行计算*
- 9.3.1 串行、并发与并行
- 9.3.2 进程与线程
- 9.3.3 多线程编程的应用
- 9.3.4 Python 多线程编程
- 9.3.5 小结
- 9.4 练习
- 第 10 章 算法设计和分析
- 10.1 枚举法
- 10.2 递归
- 10.3 分治法
- 10.4 贪心法
- 10.5 算法分析
- 10.5.1 算法复杂度
- 10.5.2 算法分析实例
- 10.6 不可计算的问题
- 10.7 练习
- 第 11 章 计算+X
- 11.1 计算数学
- 11.2 生物信息学
- 11.3 计算物理学
- 11.4 计算化学
- 11.5 计算经济学
- 11.6 练习
- 附录
- 1 Python 异常处理参考
- 2 Tkinter 画布方法
- 3 Tkinter 编程参考
- 3.1 构件属性值的设置
- 3.2 构件的标准属性
- 3.3 各种构件的属性
- 3.4 对话框
- 3.5 事件
- 参考文献