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 (&lt;Enter&gt; to quit): ") 
while x != "":
    data.append(eval(x))
    x = raw_input("Enter a number (&lt;Enter&gt; 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 (&lt;Enter&gt; to quit): ") 
    while x != "":
        d.append(eval(x))
        x = raw_input("Enter a number (&lt;Enter&gt; 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()