2.1 列表和元组能存储线性表型数据
在用Java语言学数据结构时,会了解到这样的知识:第一,数组和链表是不同数据结构的对象;第二,在数组中,查找特定索引位置元素的效率比链表快,但插入和删除元素的效率却没链表高。不过,这和Python语言中定义线性表的方式不同,所以上述知识点无法应用在Python中。
在Python语言中,数组是个统称,它有三种类型:第一类是链表型,在其中可以动态添加和删除数据,本书中称此类型为列表(List);第二类是元组型(Tuple),一旦定义后,其中的元素不能被修改;第三类是字典型(Dictionary),在这类数组中能存储若干个键-值对(Key-Value Pair)类型的数据。
2.1.1 列表的常见用法
列表是Python中常见的集合类数据结构,在下面的ListDemo.py范例程序中演示了Python数组的常见用法,需要注意的是:尽量不要在其中存储不同类型的数据。
1 # !/usr/bin/env python 2 # coding=utf-8 3 # 定义多个类型的列表 4 priceList=[10.58,25.47,100.58] # 浮点型列表 5 cityList=["ShangHai", "HangZhou", "NanJing"] # 字符串类型列表 6 mixList=[1, 3.14, "Company"] # 混合类型的列表,谨慎使用 7 8 # 在控制台输出 9 print(priceList) #[10.58, 25.47, 100.58] 10 print(cityList) #['ShangHai', 'HangZhou', 'NanJing'] 11 print(mixList) #[1, 3.14, 'Company'] 12 13 del mixList[2] 14 print(mixList) # 没有了最后一个元素 15 # mixArr.remove(2) # 去掉没有的元素,也会抛出异常 16 mixList.remove(1) 17 print(mixList) # 也看不到1了 18 19 print(priceList[0]) # 获得数组指定位置的元素,这里输出是10.58 20 priceList.append(200.74) # 添加元素 21 print(priceList) # 能看到添加后的元素 22 print(cityList.index("ShangHai")); 23 #print(cityList.index("DaLian")); # 如果找不到,会抛出异常并终止程序
在上述范例程序代码中,演示了针对列表的常见用法,通过注释可以知道关键代码的含义,而且在各个输出语句的位置也通过注释说明了输出的结果。这里,请读者注意如下的要点。
(1)在定义列表时,如果没有特殊的需求,请不要像第6行那样,在列表中定义了不同类型的数据,因为在处理时不得不先判断数据的类型再进行针对性的读取,这样会增加代码的复杂度,非常不利于程序的维护。
(2)可以通过del和remove来删除元素,但在使用remove删除元素时,需要保证该元素存在,否则就会抛出异常,从而导致程序异常中止。如果去掉第15行的注释符号,就能看到因删除不存在元素而导致抛出异常的结果。如果希望在删除不存在元素时不抛出异常,那么可以调用discard方法。
(3)可以像第19行那样,通过诸如priceList[0]的形式,以索引的方式操作其中的具体元素,这里请注意,元素的索引值是从0开始,priceArr[0]表示的是列表中的第一个元素。
(4)可以像第22行那样,用index的方式在数组里查找元素,如果找到,返回的是该元素的索引位置,同样请注意,如果去掉第23行的注释符号,去找一个不存在的元素,就会抛出异常而导致程序意外中止。
在实际的程序项目中,如果出现异常,我们期望的结果是,看到错误提示,同时程序继续执行。不过,在这个范例程序中,我们看到的是“抛出异常,程序意外中止”的结果,这种中止程序执行的做法是比较危险的,所以在本书的后续部分,会通过“异常处理机制”来专门解决这类问题。
2.1.2 链表、列表还是数组?这仅仅是叫法的不同
在2.1.1小节提到,链表类数组是数组类型的一种,所以在上一节范例程序中定义的priceArr对象,称它为链表(有人也称它列表)和数组,都不算错。事实上,Python由于是弱数据类型的语言,即定义元素时无需定义数据类型,因此从使用方式上来看,列表(List)和数组(Array)的差别确实不大。
如果大家熟悉数据结构的知识,就会发现列表和数据在底层的实现是不同的,这在Java或C#等强数据类型(即定义元素必须要给出数据类型)的语言中确实会是个问题,但在Python语言中则不是。
通过Python语言给出的接口,我们可以用同一种方式来操作列表和数组,无需也无从选择,不能像Java等语言一样,在定义时必须强制指定是数组还是列表。
既然无从选择,那么可以这样说,在Python中,数组和列表其实是相通的,也就是用起来一样,但为了不让大家混淆,本书会把列表型的数组也称为“列表”,而尽量不出现“数组”的字样。
2.1.3 对列表中元素进行操作的方法
在常见的数据分析和统计场景中使用列表,可以调用Python提供的诸如排序和求最大值等操作列表中元素的方法。在下面的ListSeniorUsage.py范例程序中,可以看到操作列表中数据的常用方法。
1 # !/usr/bin/env python 2 # coding=utf-8 3 4 priceList=[10.58,25.47,100.58,500.47] 5 cityList=["ShangHai", "HangZhou", "NanJing"] 6 # 进行排序 7 priceList.sort() 8 print(priceList); 9 # print(priceList.sort()); # 错误的用法 10 11 print(sum(priceList)) # 求和 12 print(max(priceList)) # 求最大值,输出500.47 13 print(min(cityList)) # 求最小值,输出HangZhou 14 15 subList=priceList[1:3] # 截取列表中元素 16 print(subList) # 输出[25.47, 100.58]
在第7行中,对priceList进行了排序,如果要输出排序后的列表,应该如第8行那样,而不能像第9行那样直接打印priceList.sort()。
从第11行到第13行的程序语句,分别执行了求和,求最大值和最小值的操作。其中第13行是对字符串型列表中的字符串求最小值,结果是按字母顺序排序字符串并返回最小的字符串。
第15行的程序语句是截取了列表中的部分元素,请注意,冒号前的参数表示从哪个索引位置开始截取,索引值也是从0开始,语句中的1,表示从第2个元素开始。这里需特别注意,冒号后的参数表示截取到哪个索引位置,这条语句中是3,表示截取到列表的第4个元素之前,但不含第4个元素(不含500.47这个元素)。
2.1.4 不能修改元组内的元素
在前文中,是通过方括号来定义列表,而元组(Tuple)是通过“()”来定义的。
元组内的元素不能被修改,具体含义就是:第一,不能修改和删除其中的元素;第二,创建好的元组无法再向其中添加元素。不过,可以针对整个元组进行其他操作。在实际应用中,一般用元组来存储不会变更的常量元素。在下面的TupleDemo.py代码中演示了元组的常见用法。
1 # !/usr/bin/env python 2 # coding=utf-8 3 # 定义两个元组 4 cityTup=("TianJin","WuHan","ChengDu") 5 # cityTup[0]="HeFei" # 会抛出异常 6 # del cityTup[0] # 无法删除其中的元素,则会抛出异常 7 print(cityTup) # 输出结果是('TianJin', 'WuHan', 'ChengDu') 8 # 把列表转为元组 9 bookList=["Python book","Java Book"] 10 bookTup=tuple(bookList) 11 12 # 查询操作 13 print(cityTup[1]) # 输出WuHan 14 print(cityTup[0:2]) # 输出('TianJin', 'WuHan') 15 16 # 统计元组里指定元素的个数 17 print(cityTup.count("TianJin")) # 返回1 18 # 统计元组的长度 19 print(len(cityTup)) # 返回3 20 21 # 只能删除整个元组对象 22 del cityTup
范例程序的第4行通过小括号的方式定义了一个名为cityTup的元组,第5行的程序语句企图修改这个元组的第1个元素,由于元组无法被修改,因此会抛出异常。第9行的代码定义了一个列表,之后通过第10行的tuple方法,把列表转换成了元组,这样bookTup也就无法被修改了。
在第13和第14行中,以两种方式查询了元组内的元素,请注意,第14行语句中冒号前后的两个参数也是表示存取的“开始位置”和“终止位置”,截止到终止位置之前的元素都会输出,但终止位置的元素不会被输出。
在第17行中,调用count方法统计元组内指定元素出现的次数,在第19行中,通过调用len方法打印输出元组的长度。虽然我们无法删除元组中的单个元素,但却可以通过调用del方法删除整个元组,如第22行那样。