从零开始学Python数据分析与挖掘
上QQ阅读APP看书,第一时间看更新

3.1 数据结构及方法

本节所介绍的Python数据结构,并非等同于数据库中的数据结构,而是指列表、元组和字典,它们都属于存储数据的容器。如何构建和灵活使用这三种数据结构将是本节的主要内容。

3.1.1 列表

关于列表,需要对其说明如下三点:

  • 列表的构造是通过英文状态下的方括号完成的,即[]。可以将每一个元素存放在中括号中,而且列表中的元素是不受任何限制的,可以存放数值、字符串及其他数据结构的内容。
  • 列表是一种序列,即每个列表元素是按照顺序存入的,这些元素都有一个属于自己的位置(或下标)。
  • 列表是一种可变类型的数据结构,即可以实现对列表的修改,包括增加、删除和修改列表中的元素值。

“列表是一种序列”指的是可以通过索引(或下标)的方式实现列表元素的获取,Python中的索引都是用英文状态下的方括号表示,而且,对于位置索引来说,都是从0开始。接下来通过具体的例子来解释四种常见的索引方式。

1.正向单索引

正向单索引指的是只获取列表中的某一个元素,并且是从左到右的方向数元素所在的位置,可以用[n]表示,例如:

如上结果显示,变量list1是一个含有7个元素的列表,包含字符串(注意,字符串必须用引号引起来)、数值和列表。由于位置索引是从0开始,所以索引号与实际的位置正好差1,最后使用print函数将取回的元素打印出来。列表中最后一个元素正好又是一个列表(一般称为嵌套列表),所以要取出嵌套列表中的元素就需要两层索引实现。

2.负向单索引

负向单索引是指在正向单索引的基础上添加一个负号“-”,所表达的含义是从右向左的方向获取元素,可以用[-n]表示,例如:

如果列表元素特别多,而需要获取的数据恰好又是最后几个,那么负向单索引就显得尤为方便和简单,否则从头开始数下去,就显得非常麻烦。注意,最后一个列表元素可以用[-1]表示,千万不要写成[-0],这是初学者容易犯错的地方。

3.切片索引

切片索引指的是按照固定的步长,连续取出多个元素,可以用[start:end:step]表示。start指索取元素的起始位置;end指索取元素的终止位置(注意,end位置的元素是取不到的!);step指索取元素的步长,默认为1,表示逐个取出一连串的列表元素;切片,你可以把它理解成高中所学的值域范围,属于左闭右开的效果。例如:

如上结果显示,第一个切片是逐个获取元素;第二个切片是隔元素返回;第三个切片并没有获得所有的最后三个元素,不管你把-1换成0还是换成别的值,返回的结果中都无法得到“湖北”这个元素。为解决这个末尾元素取不到的难题,我们下面介绍无限索引。

4.无限索引

无限索引是指在切片过程中不限定起始元素的位置或终止元素的位置,甚至起始和终止元素的位置都不限定,可以用[::step]表示。第一个冒号是指从列表的第一个元素开始获取;第二个冒号是指到最后一个元素结束(包含最后一个元素值)。例如:

如上结果显示,如果需要从头开始返回元素,可以将切片中的start设置为冒号(:);如果需要返回至结尾的元素,可以将切片中的end设置为冒号;当然,start和end都设置为冒号的话,返回的是整个列表元素(等同于复制的功效),再通过step控制步长,实现有规律地跳格取数。

“列表是可变类型的数据结构”指的是可以通过列表特有的“方法”,实现列表元素的增加、删除和修改,一旦通过这些方法完成列表的改操作,列表本身就发生变化了。注意,这里说的是特有的“方法”,而不是函数,对于初学者来说,不易分清Python中的“方法”和函数。

为了让读者容易理解两者的区别,这里举个形象的例子加以说明。“方法”可以理解为“婴幼儿专用商品”,写成Python的语法就是object.method,这里的object就是婴幼儿,method就是专用商品,例如儿童玩具、奶嘴、尿不湿等商品就是给婴幼儿使用的,这些商品是限定用户的,就像“方法”是限定特有对象一样;函数可以理解为普通商品,写成Python的语法就是function(object),这里的object就是普通大众,function就是大众商品,例如雨伞、自行车、米饭等商品是不限定任何人群的,就像函数可以接受任何一类参数对象一样(如所有可迭代对象)。上面是从狭义的角度简单理解两者的区别,如果从广义的角度来看“方法”和函数,它们都属于对象的处理函数。

5.列表元素的增加

如果需要往列表中增加元素,可使用Python提供的三种方法,即append、extend和insert。下面通过例子来解释三者的区别:

    list3 = [1,10,100,1000,10000]
    # 在列表末尾添加数字2
    list3.append(2)
    print(list3)

out: [1, 10, 100, 1000, 10000, 2]

append是列表所特有的方法,其他常见对象是没有这个方法的,该方法是往列表的尾部增加元素,而且每次只能增加一个元素。如果需要一次增加多个元素,该方法无法实现,只能使用列表的extend方法。

    # 在列表末尾添加20,200,2000,20000四个值
    list3.extend([20,200,2000,20000])
    print(list3)

out: [1, 10, 100, 1000, 10000, 2, 20, 200, 2000, 20000]

使用extend方法往列表尾部增加多个元素时,一定要将多个元素捆绑为列表传递给该方法,即使只有一个元素,也需要以列表的形式传递。

insert方法可以在列表的指定位置插入新值,该方法需要传递两个参数:一个是索引(或下标)参数,如上面的2,是指在列表元素的第三个位置插入;另一个参数是具体插入的值,既可以是一个常量,也可以是一个列表,如果是列表,就是以嵌套列表的形式插入。

6.列表元素的删除

能往列表中增加元素,就能从列表中删除元素。关于列表元素的删除有三种方法,分别是pop、remove和clear,下面举例说明:

    # 删除list3中20000这个元素
    list3.pop()
    print(list3)
    # 删除list3中11这个元素
    list3.pop(2)
    print(list3)
    out:
    [1, 10, 11, 100, 1000, 10000, ['a', 'b', 'c'], 2, 20, 200, 2000]
    [1, 10, 100, 1000, 10000, ['a', 'b', 'c'], 2, 20, 200, 2000]

如上结果所示,通过pop方法,可以完成列表元素两种风格的删除,一种是默认删除列表的末尾元素,另一种是删除指定位置的列表元素,而且都只能删除一个元素。

    # 删除list3中的['a', 'b', 'c']
    list3.remove(['a', 'b', 'c'])
    print(list3)
    out:
    [1, 10, 100, 1000, 10000, 2, 20, 200, 2000]

remove方法提供了删除指定值的功能,该功能非常棒,但是它只能删除首次出现的指定值。如果你的列表元素特别多,通过pop方法删除指定位置的元素就显得非常笨拙,因为你需要数出删除值的具体位置,而使用remove方法就很方便。

    # 删除list3中所有元素
    list3.clear()
    print(list3)

out: []

clear从字面理解就是清空的意思,确实,该方法就是将列表中的所有元素全部删除。如上结果所示,通过clear方法返回的是一个空列表。

7.列表元素的修改

如果列表元素值存在错误该如何修改呢?不幸的是对于列表来说,没有具体的方法可言,但可以使用“取而改之”的思想实现元素的修改。下面通过具体的例子来加以说明:

    list4 = ['洗衣机','冰响','电视机','电脑','空调']
    # 将“冰响”修改为“冰箱”
    print(list4[1])
    list4[1] = '冰箱'
    print(list4)

out: 冰响 ['洗衣机', '冰箱', '电视机', '电脑', '空调']

“取而改之”是指先通过错误元素的获取(通过索引的方法),再使用正确的值重新替换即可。正如上面的结果所示,就是用新值替换旧值,完成列表元素的修改。

当然,除了上面介绍的列表元素增加和删除所涉及的“方法”外,还有其他“方法”,如排序、计数、查询位置、逆转,接下来仍然通过具体的例子来说明它们的用法:

count方法是用来对列表中的某个元素进行计数,每次只能往count方法中传递一个值;index方法则返回指定值在列表中的位置,遗憾的是只返回首次出现该值的位置;reverse方法是将列表元素全部翻转,最后一个元素重新排到第一个位置,倒数第二个元素排到第二个位置,以此类推;sort方法可以实现列表元素的排序,默认是升序,可以将reverse参数设置为True,进而调整为降序。需要注意的是,sort方法只能对同质数据进行排序,即列表元素统一都是数值型或字符型,不可以混合多种数据类型或数据结构。

3.1.2 元组

元组与列表类似,关于元组同样需要做如下三点说明:

  • 元组通过英文状态下的圆括号构成,即()。其存放的元素与列表一样,可以是不同的数值类型,也可以是不同的数据结构。
  • 元组仍然是一种序列,所以几种获取列表元素的索引方法同样可以使用到元组对象中。
  • 与列表最大的区别是,元组不再是一种可变类型的数据结构。

由于元组只是存储数据的不可变容器,因此其只有两种可用的“方法”,分别是count和index。它们的功能与列表中的count和index方法完全一样,这里就简单举例,不再详细赘述:

    t = ('a','d','z','a','d','c','a')
    # 计数
    print(t.count('a'))
    # 元素位置
    print(t.index('c'))

out: 3 5

3.1.3 字典

字典是非常常用的一种数据结构,它与json格式的数据非常相似,核心就是以键值对的形式存储数据,关于Python中的字典做如下四点说明:

  • 构造字典对象需要使用大括号表示,即{},每一个字典元素都是以键值对的形式存在,并且键值对之间用英文状态下的冒号隔开,即key:value。
  • 键在字典中是唯一的,不能有重复,对于字符型的键需要用引号引起来。值可以是单个值,也可以是多个值构成的列表、元组或字典。
  • 字典不再是序列,无法通过位置索引完成元素值的获取,只能通过键索引实现。
  • 字典与列表一样,都是可变类型的数据结构。

首先介绍字典的键索引如何实现元素值的获取,举例如下:

对于字典来说,它不再是序列,通过第一条输出结果可知,构造时的字典元素与输出时的字典元素顺序已经发生了变化,要想获取元素值,只能在索引里面写入具体的键;在字典dict1中,键“子女”对应的值是另一个字典,属于dict1的嵌套字典,所以需要通过双层键索引获取张三儿子的姓名;键“兴趣”对应的值是列表,所以“游泳”这个值只能通过先锁定字典的键再锁定列表元素的位置才能获得。

接下来介绍字典的可变性。关于可变性,仍然是对字典元素进行增加、删除和修改的操作,这些操作都可以通过字典的“方法”实现,下面将依次介绍字典的各个操作。

1.字典元素的增加

针对字典元素的增加,可以使用如下三种方式实现,分别是setdefault方法、update方法和键索引方法:

如上结果所示,setdefault方法接受两个参数,第一个参数为字典的键,第二个参数是键对应的值;update从字面理解是对字典的更新,关于update方法完成字典元素的修改可参见后面的内容,除此,它还可以增加元素,与setdefault不同的是该方法接受的是一个字典对象;第三种方法是通过键索引实现的,如果原字典中没有指定的键,就往字典中增加元素,否则,起到修改字典元素的功能。

2.字典元素的删除

关于字典元素的删除可以使用pop、popitem和clear三种“方法”实现,具体操作如下:

如上结果显示,pop方法在列表中同样起到删除元素的作用,如果不传递任何值给pop方法,则表示删除列表末尾的一个元素,否则就是删除指定下标的一个元素,但是在字典中pop方法必须指定需要删除的键,否则就会引起语法错误;如果需要删除嵌套字典中的某个键,就必须先通过键索引取出对应的字典,然后使用pop方法完成嵌套字典元素的删除;popitem方法不需要传递任何值,它的功能就是任意删除字典中的某个元素;clear方法则可以干净利落地清空字典中的所有元素。

3.字典元素的修改

最后来看一下字典元素的修改,关于修改部分,可以使用如下两种方法:

正如“字典元素的增加”部分所提到的,也可以使用update方法和键索引方法完成字典元素的修改,具体如上面的例子所示。需要注意的是,如果字典中的值是另一个字典或列表,需要先通过键索引实现字典元素的查询,然后在查询的基础上应用对应的修改方法即可(如update方法或“取而改之”的方法)。

列表还有一些其他“方法”,这里列出几个比较重要的方法并通过例子来解释:

get方法的功能与键索引已知,可以从字典中取出键对应的值。所不同的是,如果某个键在字典中不存在,应用键索引的方法会产生“键错误”的信息;而get方法则不会报错,也就不会影响其他脚本的正常执行。keys、values和items方法分别取出字典中的所有键、值和键值对。