用AI赚第一桶💰低成本搭建一套AI赚钱工具,源码可二开。 广告
# 第二章 列表和元组 > 来源:http://www.cnblogs.com/Marlowes/p/5293195.html > 作者:Marlowes 本章将引入一个新的概念:_数据结构_。数据结构是通过某种方式(例如对元素进行编号)组织在一起的数据元素的集合,这些数据元素可以是数字或者字符,甚至可以是其他数据结构。在Python中,最基本的数据结构是_序列_(sequence),序列中的每个元素被分配一个序号——即元素的位置,也称为_索引_。第一个索引是`0`,第二个则是`1`,以此类推。 _注:日常生活中,对某些东西计数或者编号的时候,可能会从`1`开始。所以Python使用的编号机制可能看起来很奇怪,但这种方法其实非常自然。在后面的章节中可以看到,这样做的一个原因是也可以从最后一个元素开始计数;序列中的最后一个元素标记为`-1`,倒数第二个元素为`-2`,以此类推。这就意味着我们可以从第一个元素向前或向后计数了,第一个元素位于最开始,索引为`0`.使用一段时间后,读者就会习惯于这种计数方式了。_ 本章首先对序列作一个概览,接下来讲解对所有序列(包括元组和列表)都通用的操作。这些操作也同样适用于字符串。尽管下一章才会全面介绍有关字符串操作的内容,但是本章的一些例子已经用到了字符串操作。 在完成了基本介绍后,会开始学习如何使用列表,同时看看它有什么特别之处。然后讨论元组。元组除了不能更改之外,其他的性质和列表都很类似。 ## 2.1 序列概述 Python包含6中內建的序列,本章重点讨论最常用的两种类型:_列表_和_元组_。其他的內建序列类型有字符串(将在下一章再次讨论)、`Unicode`字符串、`buffer`对象和`xrange`对象。 列表和元组的主要却别在于:列表可以修改,元组则不能。也就是说如果要根据要求来添加元素,那么列表可能会更好用;而处于某些原因,序列不能修改的时候,使用元组则更为合适。使用后者通常是技术性的,它与Python内部的运作方式有关。这也是內建函数会返回元组的原因。一般来说,在自己编写的程序中,几乎在所有的情况下都可以用列表代替元组(第四章将会介绍一个需要注意的例外情况:使用元组作为字典的键。在这种情况下,因为键不可更改,所以就不能使用列表)。 在需要操作一组数值的时候,序列很好用。可以用序列表示数据库中一个人的信息——第1个元素是姓名,第2个元素是年龄。根据上述内容编写一个列表(列表的各个元素通过逗号分隔,写在方括号中),如下所示: ``` >>> info = ["XuHoo", 19] ``` 同时,序列也可以包含其他的序列,因此,构建如下的一个人员信息的列表也是可以的,这个列表就是你的数据库: ``` >>> user_1 = ["XuHoo", 19] >>> user_2 = ["Marlowes", 19] >>> database = [user_1, user_2] >>> database [['XuHoo', 19], ['Marlowes', 19]] ``` _注:Python之中还有一种名为容器(container)的数据结构。容器基本上是包含到其他对象的任意对象。序列(例如列表和元组)和映射(例如字典)是两类主要的容器。序列中的每个元素都有自己的编号,而映射中的每个元素则有一个名字(也称为键)。在第四章会介绍更多有关映射的知识。至于既不是序列也不是映射的容器类型,集合(`set`)就是一个例子,请参见第十章的相关内容。_ ## 2.2 通用序列操作 所有序列类型都可以进行某些特定的操作。这些操作包括:_索引(indexing)、分片(slicing)、加(adding)、乘(multiplying)_以及检查某个元素是否属于序列的成员_(成员资格)。_除此之外,Python还有计算序列长度、找出最大元素和最小元素的內建函数。 _注:本节有一个重要的操作没有提到——迭代(iteration)。对序列进行迭代的意思是:依次对序列中的每个元素重复执行某些操作。更多信息请参见5.5节。_ ### 2.2.1 索引 序列中的所有元素都是有编号的——从`0`开始递增。这些元素可以通过编号分别访问,如下例所示: ``` >>> greeting = "Hello" >>> greeting[0] 'H' ``` _注:字符串就是一个由字符组成的序列。索引`0`指向第1个元素,在这个例子中就是字母`H`。_ 这就是_索引_。可以通过索引获取元素。所有序列都可以通过这种方式进行索引。使用负数索引时,Python会从右边,也就是从最后1个元素开始计数。最后1个元素的位置编号是`-1`(不是`-0`,因为那会和第1个元素重合): ``` >>> greeting[-1] 'o' ``` 字符串字面值(就此而言,其他序列字面量亦可)能够直接使用索引,而不需要一个变量引用它们。两种做法的效果是一样的: ``` >>> "Hello"[-1] # String 'o' >>> ["H", "e", "l", "l", "o"][-1] # List 'o' ``` 如果一个函数调用返回一个序列,那么可以直接对返回结果进行索引操作。例如,假设你只对用户输入年份的第四个数字感兴趣,那么,可以进行如下操作: ``` >>> fourth = raw_input("Year: ")[3] Year: 1997 >>> fourth '7' ``` 代码清单2-1是一个示例程序,它要求输入年、月(1~12的数字)、日(1~31),然后打印出相应日期的月份名称,等等。 ``` 1 #!/usr/bin/env python 2 # coding=utf-8 3 4 # 根据给定的年月日,以数字形式打印出日期 5 months = [ 6 "January", 7 "February", 8 "March", 9 "April", 10 "May", 11 "June", 12 "July", 13 "August", 14 "September", 15 "October", 16 "November", 17 "December" 18 ] 19 20 # 以1~31的数字作为结尾的列表 21 endings = ["st", "nd", "rd"] + 17 * ["th"] \ 22 + ["st", "nd", "rd"] + 7 * ["th"] \ 23 + ["st"] 24 25 year = raw_input("Year: ") 26 month = raw_input("Month(1~12): ") 27 day = raw_input("Day(1~31): ") 28 29 month_number = int(month) 30 day_number = int(day) 31 32 # 记得要将月份和天数减1,以获得正确的索引 33 month_name = months[month_number - 1] 34 ordinal = day + endings[day_number - 1] 35 36 print month_name + " " + ordinal + ", " + year ``` Code_Listing 2-1 以下是程序执行的一部分结果: ``` Year: 1997 Month(1~12): 9 Day(1~31): 10 September 10th, 1997 ``` ### 2.2.2 分片 与使用索引来访问单个元素类似,可以使用_分片操作_来访问一定范围内的元素。分片通过冒号隔开的两个索引来实现: ``` >>> tag = '<a href="http://www.python.org">Python web site</a>' >>> tag[9:30] 'http://www.python.org' >>> tag[32:-4] 'Python web site' ``` 分片操作对于提取序列的一部分是很用的。而编号在这里显得尤为重要。第1个索引是要提取的第1个元素的编号,而最后的索引则是分片之后剩余部分的第1个元素的编号。 ``` >>> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] >>> numbers[3:6] [4, 5, 6] >>> numbers[0:1] [1] ``` 简而言之,分片操作的实现需要提供两个索引作为边界,第1个索引的元素是包含在分片内的,而第2个则不包含在分片内。 1\. 优雅的捷径 假设需要访问最后3个元素(根据先前的例子),那么当然可以进行显示的操作: ``` >>> numbers[7:10] [8, 9, 10] ``` 现在,索引`10`指向的是第11个元素——这个元素并不存在,却是在最后一个元素之后(为了让分片部分能够包含列表的最后一个元素,必须提供最后一个元素的下一个元素所对应的索引作为边界)。明白了吗? 现在,这样的做法是可行的。但是,如果需要从列表的结尾开始计数呢? ``` >>> numbers[-3:-1] [8, 9] ``` 看来并不能以这种方式访问最后的元素。那么使用索引`0`作为最后一步的下一步操作所使用的元素,结果又会怎么样呢? ``` >>> numbers[-3:0] [] ``` 这并不是我们所要的结果。实际上,只要分片中最左边的索引比它右边的晚出现在序列中(在这个例子中是倒数第3个比第1个晚出现),结果就是一个空的序列。幸好,可以使用一个捷径:如果分片所得部分包括序列结尾的元素,那么,只需置空最后一个索引即可。 ``` >>> numbers[-3:] [8, 9, 10] ``` 这种方法同样适用于序列开始的元素: ``` >>> numbers[:3] [1, 2, 3] ``` 实际上,如果需要复制整个序列,可以将_两个_索引都置空: ``` >>> numbers[:] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ``` 代码清单2-2是一个小程序,它会提示输入URL(假设它的形式为 http://www.somedomainname.com ),然后提取域名。 ``` 1 #!/usr/bin/env python 2 # coding=utf-8 3 4 # 对http://www.something.com形式的URL进行分割 5 6 url = raw_input("Please enter the URL: ") 7 domain = url[11:-4] 8 9 print "Domain name: " + domain ``` Code_Listing 2-2 以下是程序运行的示例: ``` Please enter the URL: http://www.python.org Domain name: python ``` 2.更大的步长 进行分片的时候,分片的开始和结束点需要进行指定(不管是直接还是间接)。而另外一个参数(在Python2.3加入到内建类型)——步长(step length)——通常都是隐式设置的。在普通的分片中,步长是`1`——分片操作就是按照这个步长逐个遍历序列的元素,然后返回开始和结束点之间的所有元素。 ``` >>> numbers[0:10:1] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ``` 在这个例子中,分片包含了另外一个数字。没错,这就是步长的显示设置。如果步长被设置为比1大的数,那么就会跳过某些元素。例如,步长设为2的分片包括的是从头开始到结束每隔1个的元素。 ``` >>> numbers[0:10:2] [1, 3, 5, 7, 9] >>> numbers[3:6:3] [4] ``` 之前提及的捷径也可以使用。如果需要将每4个元素中的第1个提取出来,那么只要将步长设置为`4`即可: ``` >>> numbers[::4] [1, 5, 9] ``` 当然,步长不能为`0`(那不会执行),但步长可以是_负数_,此时分片从右到左提取元素: ``` >>> numbers[8:3:-1] [9, 8, 7, 6, 5] >>> numbers[10:0:-2] [10, 8, 6, 4, 2] >>> numbers[0:10:-2] [] >>> numbers[::-2] [10, 8, 6, 4, 2] >>> numbers[5::-2] [6, 4, 2] >>> numbers[:5:-2] [10, 8] ``` 在这里要得到正确的分片结果需要动些脑筋。开始点的元素(最左边的元素)包括在结果之中,而结束点的元素(最右边的元素)则不在分片之内。当使用一个负数作为步长时,必须让开始点(开始索引)_大于_结束点。在没有明确指定开始点和结束点的时候,正负数的使用可能会带来一些混淆。不过在这种情况下Python会进行正确的操作:对于一个正数步长,Python会从序列的头部开始向右提取元素,直到最后一个元素;而对于负数步长,则是从序列的尾部开始向左提取元素,直到第一个元素。 ### 2.2.3 序列相加 通过使用加运算符可以进行序列的连接操作: ``` >>> [1, 2, 3] + [4, 5, 6] [1, 2, 3, 4, 5, 6] >>> "Hello, " + "world!" 'Hello, world!' >>> [1, 2, 3] + "wrold!" Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: can only concatenate list (not "str") to list ``` 正如错误信息所提示的,列表和字符串是无法连接在一起的,尽管它们都是序列。简单来说,两种相同类型的序列才能进行连接操作。 ### 2.2.4 乘法 用数字`x`乘以一个序列会生成新的序列,而在新的序列中,原来的序列将被重复`x`次。 ``` >>> "Python" * 5 'PythonPythonPythonPythonPython' >>> [19] * 10 [19, 19, 19, 19, 19, 19, 19, 19, 19, 19] ``` None、空列表和初始化 空列表可以简单地通过两个中括号进行表示(`[]`)——里面什么东西都没有。但是,如果想创建一个占用十个元素空间,却不包括任何有用内容的列表,又该怎么办呢?可以像前面那样使用`[19]*10`,或者使用`[0]*10`,这会更加实际一些。这样就生成了一个包括10个0的列表。然而,有时候可能会需要一个值来代表空值——意味着没有在里面放置任何元素。这个时候就需要使用`None`。`None`是一个Python的内建值,它的确切含义是“这里什么也没有”。因此,如果想初始化一个长度为`10`的列表,可以按照下面的例子来实现: ``` >>> sequence = [None] * 10 >>> sequence [None, None, None, None, None, None, None, None, None, None] ``` 代码清单2-3的程序会在屏幕上打印一个由字符组成的“盒子”,而这个“盒子”在屏幕上居中而且能根据用户输入的句子自动调整大小。 代码可能看起来很复杂,但只使用基本的算法——计算出有多少个空格、破折号等字符,然后将它们放置到合适的位置即可。 ``` 1 #!/usr/bin/env python 2 # coding=utf-8 3 4 # 以正确的宽度在居中的“盒子”内打印一个句子 5 6 # 注意,整数除法运算符(//)只能用在Python2.2以及后续的版本,在之前的版本中,只能使用普通除法(/) 7 8 sentence = raw_input("Sentence: ") 9 10 screen_width = 80 11 text_width = len(sentence) 12 box_width = text_width + 6 13 left_margin = (screen_width - box_width) // 2 14 15 print 16 print " " * left_margin + "+" + "-" * (box_width - 2) + "+" 17 print " " * left_margin + "| " + " " * text_width + " |" 18 print " " * left_margin + "| " + sentence + " |" 19 print " " * left_margin + "| " + " " * text_width + " |" 20 print " " * left_margin + "+" + "-" * (box_width - 2) + "+" 21 print ``` Code_Listing 2-3 下面是该例子的运行情况: ``` Sentence: He's a very naughty boy! +----------------------------+ | | | He's a very naughty boy! | | | +----------------------------+ ``` ### 2.2.5 成员资格 为了检查一个值是否在序列中,可以使用`in`运算符。该运算符和之前已经讨论过的(例如`+`、`*`运算符)有一点不同。这个运算符检查某个条件是否为真,然后返回相应的值:条件为真返回`True`,条件为假返回`False`。这样的运算符叫做_布尔运算符_,而返回的值叫做_布尔值_。第五章的条件语句部分会介绍更多关于布尔表达式的内容。 以下是一些使用了in运算符的例子: ``` >>> permissions = "rw" >>> "w" in permissions True >>> "x" in permissions False >>> users = ["mlh", "foo", "bar"] >>> raw_input("Please enter your user name: ") in users # 用户名存在 Please enter your user name: mlh True >>> raw_input("Please enter your user name: ") in users # 用户名不存在 Please enter your user name: Marlowes False >>> subject = "$$$ Get rich now!!! $$$" >>> "$$$" in subject True ``` 最初的两个例子使用了成员资格测试分别来检查`"w"`和`"x"`是否出现在字符串`permissions`中。在UNIX系统中,这两行代码可以作为查看文件可写和可执行权限的脚本。接下来的例子则是检查所提供的用户名是否存在用户列表中。如果程序需要执行某些安全策略,那么这个检查就派上用场了(在这种情况下,可能还需要使用密码)。最后一个例子可以作为垃圾邮件过滤器的一部分,它可以检查字符串`subject`是否包含字符串`"$$$"`。 _注:最后一个检查字符串是否包含`"$$$"`的例子有些不同。一般来说,`in`运算符会检查每一个对象是否为某个序列(或者是其他数据集合)的成员(也就是元素)。然后,字符串唯一的成员或者元素就是它的字符。下面的例子就说明了这一点:_ ``` >>> "P" in "Python" True ``` _实际上,在早期的Python版本中,以上代码是唯一能用于字符串成员资格检查的方法——也就是检查某个字符是否存在于一个字符串中。如果尝试去检查更长的子字符串(例如"$$$"),那么会得到一个错误信息(这个操作会引发TypeError,即类型错误)。为了实现这个功能,我们必须使用相关的字符串方法。第三章会介绍更多相关的内容。但是从Python2.3起,`in`运算符也能实现这个功能了。_ 代码清单2-4给出了一个查看用户输入的用户名和PIN码是否存在于数据库(实际上是一个列表)中的程序。如果用户名/PIN码这一数值对存在于数据库中,那么就在屏幕上打印`"Access granted"`(第一章已经提到过`if`语句,第五章还将对其进行全面讲解)。 ``` 1 #!/usr/bin/env python 2 3 # 检查用户名和PIN码 4 5 database = [ 6 ["albert", "123"], 7 ["dilbert", "3521"], 8 ["smith", "6542"], 9 ["jones", "5634"] 10 ] 11 12 username = raw_input("Please enter your username: ") 13 pin = raw_input("Please enter your PIN: ") 14 15 if [username, pin] in database: 16 print "Access granted" ``` Code_Listing 2-4 ### 2.2.6 长度、最小值和最大值 内建函数`len`、`min`和`max`非常有用。`len`函数返回序列中所包含元素的数量,`min`函数和`max`函数则分别返回序列中最大和最小元素(在第五章的“比较运算符”部分会更加详细介绍对象比较的内容)。 ``` >>> numbers = [100, 34, 678] >>> len(numbers) 3 >>> max(numbers) 678 >>> min(numbers) 34 >>> max(2, 3) 3 >>> min(9, 3, 2, 5) 2 ``` 根据上述解释,我们可以很容易地理解例子中的各个操作是如何实现的,除了最后两个表达式可能会让人有些迷惑。在这里,`max`函数和`min`函数的参数并不是一个序列,而是以多个数字直接作为参数。 ## 2.3 列表:Python的“苦力” 在前面的例子中已经用了很多次列表,它的强大之处不言而喻。本节会讨论列表不同于元组和字符串的地方:列表是可变的——可以改变列表的内容,并且列表有很多有用的、专门的方法。 ### 2.3.1 list函数 因为字符串不能像列表一样被修改,所以有时根据字符串创建列表会很有用。`list`函数(它实际上是一种_类型_而不是函数,但在这里两者的区别并不重要)可以实现这个操作: ``` >>> list("Hello") ['H', 'e', 'l', 'l', 'o'] ``` 注意,`list`函数适用于所有类型的序列,而不只是字符串。 _注:可以用下面的表达式将一个由字符(如前面代码中的)组成的列表转换为字符串:_ ``` ''.join(somelist) ``` _在这里,`somelist`是需要转换的列表。要了解这行代码真正的含义,请参考第三章有关`join`函数的部分。_ ### 2.3.2 基本的列表操作 列表可以使用所有适用于序列的标准操作,例如索引、分片、连接和乘法。有趣的是,列表是可以修改的。本节会介绍一些可以改变列表的方法:_元素赋值、元素删除、分片赋值以及列表方法_(请注意,并不是所有的列表方法都能真正地改变列表)。 1.改变列表:元素赋值 改变列表是很容易的,只需要使用第一章提到的普通赋值语句即可。然而,我们并不会使用`x=2`这样的语句进行赋值,而是使用索引标记来为某个特定的、位置明确的元素赋值。如`x[1]=2`。 ``` >>> x = [1, 1, 1] >>> x[1] = 2 >>> x [1, 2, 1] ``` _注:不能为一个位置不存在的元素进行赋值。如果列表的长度为`2`,那么不能为索引为`100`的元素进行赋值。如果要那样做,就必须创建一个长度为`101`(或者更长)的列表。请参考本章“`None`、空列表和初始化”一节。_ 2.删除元素 从列表中删除元素也很容易:使用`del`语句实现。 ``` >>> names = ["Alice", "Beth", "Cecil", "Dee-Dee", "Earl"] >>> del names[2] >>> names ['Alice', 'Beth', 'Dee-Dee', 'Earl'] ``` 注意`Cecil`是如何彻底消失的,并且列表的长度也从`5`变为了`4`。除了删除列表中的元素,`del`语句还能用于删除其他元素。它可以用于字典元素(请参考第四章)甚至是其他变量得删除操作,有关这方面的详细介绍,请参见第五章。 3.分片赋值 分片是一个非常强大的特性,分片赋值操作则更加显现它的强大。 ``` >>> name = list("Perl") >>> name ['P', 'e', 'r', 'l'] >>> name[2:] = list("ar") >>> name ['P', 'e', 'a', 'r'] ``` 程序可以一次为多个元素赋值了。可能有的读者会想:这有什么大不了的,难道就不能一次一个地赋吗?当然可以,但是在使用分片赋值时,可以使用与原序列不等长的序列将分片替换: ``` >>> name = list("Perl") >>> name[1:] = list("ython") >>> name ['P', 'y', 't', 'h', 'o', 'n'] ``` 分片赋值语句可以在不需要替换任何原有元素的情况下_插入_新的元素: ``` >>> numbers = [1, 5] >>> numbers[1:1] = [2, 3, 4] >>> numbers [1, 2, 3, 4, 5] ``` 这个程序只是“替换”了一个空的分片,因此实际的操作是插入了一个序列。以此类推,通过分片赋值来删除元素也是可行的。 ``` >>> numbers [1, 2, 3, 4, 5] >>> numbers[1:4] = [] >>> numbers [1, 5] ``` 上面的例子结果和`del numbers[1:4]`的一样。接下来请读者自己尝试利用1之外的步长,甚至是负数进行分片吧。 ### 2.3.3 列表方法 之前的章节中已经介绍了什么是函数,那么现在来看看另外一个与函数密切相关的概念——_方法。_ 方法是一个与某些对象有紧密联系的函数,对象可能是列表、数字,也可能是字符串或者其他类型的对象。一般来说,方法可以这样进行调用: _对象.方法(参数)_ 除了对象被放置到方法名之前,并且两者之间用一个点号隔开,方法调用与函数调用很类似。第七章将对方法到底是什么进行更详细的解释。列表提供了几个方法,用于检查或者修改其中的内容。 1\. `append` `append`方法用于在列表末尾追加新的对象: ``` >>> lst = [1, 2, 3] >>> lst.append(4) >>> lst [1, 2, 3, 4] ``` 为什么我选择了如此糟糕的变量名`lst`,而不是使用`list`来表示一个列表呢?原因在于`list`是一个内建函数(实际上,从Python2.2开始,`list`就是一个类型而不是函数了。(`tuple`和`str`也是如此)如果想了解完整的说明,请参见9.3.2节)。如果使用`list`作为变量名,我就无法调用`list`函数了。根据给定的应用程序可以定义更好的变量名,像`lst`这样的变量名是毫无意义的。所以,如果需要定义一个价格列表,那么就应该使用`prices`、`prices_of_eggs`,或者`pricesOfEggs`作为变量名。 注意,下面的内容很重要:`append`方法和其他一些方法类似,只是在恰当位置修改原来的列表。这意味着,它不是简单地返回一个修改过的新列表——而是直接修改原来的列表。一般来说这正是你想要的,但是在某些情况下,这样也会带来其他麻烦。在本章稍后讲述`sort`方法时,我将再次讨论这个问题。 2\. `count` `count`方法统计某个元素在列表中出现的次数: ``` >>> ["to", "be", "or", "not", "to", "be"].count("to") 2 >>> x = [[1, 2], 1, 1, [2, 1, [1, 2]]] >>> x.count(1) 2 >>> x.count([1, 2]) 1 ``` 3\. `extend` `extend`方法可以在列表的末尾一次性追加另一个序列中的多个值。换句话说,可以用新的列表扩展原有的列表: ``` >>> a = [1, 2, 3] >>> b = [4, 5, 6] >>> a.extend(b) >>> a [1, 2, 3, 4, 5, 6] ``` 这个操作看起来很像连接操作,两者最主要的区别在于:`extend`方法修改了被扩展的序列(在这个例子中,就是`a`)。而原始的连接操作则不然,它会返回一个全新的列表: ``` >>> a = [1, 2, 3] >>> b = [4, 5, 6] >>> a + b [1, 2, 3, 4, 5, 6] >>> a [1, 2, 3] ``` 你可以看到连接的列表与之前例子中被扩展的列表是一样的,但是这一次它并没有被修改。这是因为原始的连接操作创建了一个包含`a`和`b`副本的新列表。如果需要如下例所示的操作,那么连接操作的效率会比`extend`方法低。 ``` >>> a = a + b ``` 同样,这里也不是一个原位置操作,它并不会修改原来的列表。 我们可以使用分片赋值来实现相同的结果: ``` >>> a = [1, 2, 3] >>> b = [4, 5, 6] >>> a[len(a):] = b >>> a [1, 2, 3, 4, 5, 6] ``` 虽然这么做是可行的,但是代码的可读性就不如使用`extend`方法了。 4\. `index` `index`方法用于从列表中找出某个值第一个匹配项的索引位置: ``` >>> knights = ["We", "are", "the", "knights", "who", "say", "ni"] >>> knights.index("who") 4 >>> knights.index("herring") Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: 'herring' is not in list ``` 当搜索单词`who`的时候,就会发现它在索引号为`4`的位置。然而,当搜索`"herring"`的时候,就会引发一个异常,因为这个单词没有被找到。 5\. `insert` `insert`方法用于将对象插入列表中: ``` >>> numbers = [1, 2, 3, 5, 6, 7] >>> numbers.insert(3, "four") >>> numbers [1, 2, 3, 'four', 5, 6, 7] ``` 与`extend`方法一样,`insert`方法的操作也可以用分片赋值来实现。 ``` >>> numbers = [1, 2, 3, 5, 6, 7] >>> numbers[3:3] = ["four"] >>> numbers [1, 2, 3, 'four', 5, 6, 7] ``` 这样做有点新奇,但是它的可读性绝对不如`insert`方法。 6\. `pop` `pop`方法会移除列表中的一个元素(默认是最后一个),并且返回该元素的值: ``` >>> x = [1, 2, 3] >>> x.pop() 3 >>> x [1, 2] >>> x.pop(0) 1 >>> x [2] ``` _注:`pop`方法是唯一一个既能修改列表又返回元素值(除了`None`)的列表方法。_ 使用`pop`方法可以实现一种常见的数据结构——_栈_。栈的原理就像堆放盘子那样。只能在顶部放盘子,同样,也只能从顶部拿走一个盘子。最后被放入栈堆的最先被移除(这个原则成为LIFO,即_后进先出_)。 对于上述的两个栈操作(放入和移出),它们有大家都认可的称谓——_入栈_(`push`)和_出栈_(`pop`)。Python没有_入栈_方法,但可以使用`append`方法来代替。`pop`方法和`append`方法的操作结果恰好相反,如果_入栈_(或者追加)刚刚_出栈_的值,最后得到的结果还是原来的栈。 ``` >>> x = [1, 2, 3] >>> x.append(x.pop()) >>> x [1, 2, 3] ``` _注:如果需要实现一个先进先出(FIFO)的队列(`queue`),那么可以使用`insert(0, ...)`来代替`append`方法。或者,也可以继续使用`append`方法,但必须用`pop(0`)来代替`pop()`。更好的解决方案是使用`collection`模块中的`deque`对象。要了解更详细的信息,请参见第十章_ 7\. `remove` `remove`方法用于移除列表中某个值的第一个匹配项: ``` >>> x = ["to", "be", "or", "not", "to", "be"] >>> x.remove("be") >>> x ['to', 'or', 'not', 'to', 'be'] >>> x.remove("bee") Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: list.remove(x): x not in list ``` 可以看到:只有第一次出现的值被移除了,而不存在于列表中的值(比如例子中的`"bee"`)是不会移除的。 值得注意的是,`remove`是一个没有返回值的原位置改变的方法。它修改了列表却没有返回值,这与`pop`方法相反。 8\. `reverse` `reverse`方法将列表中的元素反向存放(我猜你们对此不会特别惊讶): ``` >>> x = [1, 2, 3] >>> x.reverse() >>> x [3, 2, 1] ``` 请注意,该方法也改变了列表但不反悔值(就像`remove`和`sort`)。 _注:如果需要对一个序列进行反向迭代,那么可以使用`reversed`函数。这个函数并不返回一个列表,而是返回一个迭代器(`iterator`)对象(第九章介绍了更多关于迭代器的内容)。尽管如此,使用`list`函数把返回值的对象转换成列表也是可行的:_ ``` >>> x = [4, 6, 2, 1, 7, 9] >>> list(reversed(x)) [9, 7, 1, 2, 6, 4] ``` 9.sort `sort`方法用于在原位置(从Python2.3开始,`sort`方法使用了固定的排序算法)对列表进行排序。在“原位置排序”意味着改变原来的列表,从而让其中的元素未能按一定的顺序排列,而不是简单地返回一个已排序的列表副本。 ``` >>> x = [4, 6, 2, 1, 7, 9] >>> x.sort() >>> x [1, 2, 4, 6, 7, 9] ``` 前面介绍过了几个改变列表却不返回值的方法,在大多数情况下这样的行为方式是很合常理的(例如`append`方法)。但是,`sort`方法的这种行为方式需要重点讲解一下,因为很多人都被`sort`方法弄糊涂了。当用户需要一个排好序的列表副本,同时又保留原有列表不变的时候,问题就出现了。为了实现这个功能,我们自然而然就想到了如下的做法(实际是错误的): ``` >>> x = [4, 6, 2, 1, 7, 9] >>> y = x.sort() # Don't do this! >>> print y None ``` 因为`sort`方法修改了x却返回了空值,那么最后得到的是已排序的`x`以及值为`None`的`y`。实现这个功能的正确方法是,首先把`x`的副本赋值给`y`,然后对`y`进行排序,如下例所示: ``` >>> x = [4, 6, 2, 1, 7, 9] >>> y = x[:] >>> y.sort() >>> x [4, 6, 2, 1, 7, 9] >>> y [1, 2, 4, 6, 7, 9] ``` 再次调用`x[:]`得到的是包含了x所有元素的分片,这是一种很有效率的赋值整个列表的方法。只是简单地把`x`赋值给`y`是没有用的,因为这样做就让`x`和`y`都指向同一个列表了。 ``` >>> x = [4, 6, 2, 1, 7, 9] >>> y = x >>> y.sort() >>> x [1, 2, 4, 6, 7, 9] >>> y [1, 2, 4, 6, 7, 9] ``` 另一种获取已排序的列表副本的方法是,使用`sorted`函数: ``` >>> x = [4, 6, 2, 1, 7, 9] >>> y = sorted(x) >>> x [4, 6, 2, 1, 7, 9] >>> y [1, 2, 4, 6, 7, 9] ``` 这个函数实际上可以用于任何序列,却总是返回一个列表(`sorted`函数可以用于任何可迭代的对象。有关可迭代对象的详细内容,请参见第九章): ``` >>> sorted("Python") ['P', 'h', 'n', 'o', 't', 'y'] ``` 如果想把一些元素按相反的顺序排序,可以先使用`sort`(或者`sorted`),然后再调用`reverse`方法(注意,需要分两次对列表调用`sort`方法以及`reverse`方法。如果打算通过`x.sort().reverse()`来实现,会发现行不通,因为`x.sort()`返回的是`None`。当然,`sorted(x).reverse()`是正确的做法),或者也可以使用`reverse`参数,下一节将对此进行描述。 10.高级排序 如果希望元素能按照特定的方式进行排序(而不是`sort`函数默认的方式,即根据Python的默认排序规则按升序排列元素,第五章内对此进行详解),那么可以通过`compare(x, y)`的形式自定义_比较函数_。`compare(x, y)`函数会在` x < y` 时返回负数,在 `x > y` 时返回正数,如果 `x = y` 则返回0(根据你的定义)。定义好该函数之后,就可以提供给`sort`方法作为参数了。内建函数`cmp`提供了比较函数的默认实现方式: ``` >>> cmp(42, 32) 1 >>> cmp(99, 100) -1 >>> cmp(19, 19) 0 >>> numbers = [5, 2, 9, 7] >>> numbers.sort(cmp) >>> numbers [2, 5, 7, 9] ``` `sort`方法有另外两个可选的参数——`key`和`reverse`。如果要使用它们,那么就要通过名字来指定(这叫做_关键字参数_,请参见第六章以了解更多的内容)。参数`key`与参数`cmp`类似——必须提供一个在排序过程中使用的函数。然而,该函数并不是直接用来确定对象的大小,而是为每个元素创建一个_键_,然后所有元素根据键来排序。因此,如果要根据元素的长度进行排序,那么可以使用`len`作为键函数: ``` >>> x = ["aardvark", "abalone", "acme", "add", "aerate"] >>> x.sort(key=len) >>> x ['add', 'acme', 'aerate', 'abalone', 'aardvark'] ``` 另一个关键字参数`reverse`是简单的布尔值(`True`或者是`False`。第五章会讲述更详细的内容),用来指明列表是否要进行反向排序。 ``` >>> x = [4, 6, 2, 1, 7, 9] >>> x.sort(reverse=True) # True为反向排序 >>> x [9, 7, 6, 4, 2, 1] >>> x = [4, 6, 2, 1, 7, 9] >>> x.sort(reverse=False) # Flase为正向排序 >>> x [1, 2, 4, 6, 7, 9] ``` `cmp`、`key`、`reverse`参数都可以用于`sorted`函数。在多数情况下,为`cmp`或`key`提供自定义函数是非常有用的。第六章将会讲述如何定义自己的函数。 注:如果想了解更多有关于排序的内容,可以查看Andrew Dalke的 “Sorting Mini-HOWTO”,链接是:http://wiki.python.org/moin/HowTo/Sorting 。 ## 2.4 元组:不可变序列 元组与列表一样,也是一种序列。唯一的不同是元组不能修改(元组和列表在技术实现上有一些不同,但是在实际使用时,可能不会注意到。而且,元组没有像列表一样的方法。)。(你可能注意到了,字符串也是如此)创建元组的语法很简单:如果你用逗号分隔了一些值,那么你就自动创建了元组。 ``` >>> 1, 2, 3 (1, 2, 3) ``` 元组也是(大部分时候是)通过圆括号括起来的: ``` >>> (1, 2, 3) (1, 2, 3) ``` 空元组可以用没有包含内容的两个圆括号来表示: ``` >>> () () ``` 那么如何实现包括一个值的元组呢。实现方法有些奇特——必须加个逗号,即使只有一个值: ``` >>> 42 42 >>> 42, (42,) >>> (42,) (42,) ``` 最后两个例子生成了一个长度为1的元组,而第一个例子根本不是元组。逗号是很重要的,只添加圆括号也是没用的:`(42)`和`42`是完全一样的。但是,一个逗号却能彻底地改变表达式的值: ``` >>> 3 * (40 + 2) 126 >>> 3 * (40 + 2,) (42, 42, 42) ``` ### 2.4.1 tuple函数 `tuple`函数的功能与`list`函数基本上是一样的:以一个序列作为参数并把它转换为元组(`tuple`并不是真正的函数——而是一种类型。在之前讲述`list`函数的时候,我也提到了这一点。同时,与`list`函数一样,目前也可以放心地忽略这一点)。如果参数就是元组,那么该参数就会被原样返回: ``` >>> tuple([1, 2, 3]) (1, 2, 3) >>> tuple("abc") ('a', 'b', 'c') >>> tuple((1, 2, 3)) (1, 2, 3) ``` ### 2.4.2 基本元组操作 元组其实并不复杂——除了创建元组和访问元组元素之外,也没有太多其他的操作,可以参照其他类型的序列来实现: ``` >>> x = 1, 2, 3 >>> x[1] 2 >>> x[0:2] (1, 2) ``` 元组的分片还是元组,就像列表的分片还是列表一样。 ### 2.4.3 那么,意义何在 现在你可能会想到底有谁会需要像元组那样的不可变序列呢?难道就不能在不改变其中内容的时候坚持只用列表吗?一般来说这是可行的。但是由于以下两个重要的原因,元组是不可替代的。 √ 元组可以在映射(和集合的成员)中当做键使用——而列表则不行(本章导言部分提到过映射,更多有关映射的内容,请参看第四章)。 √ 元组作为很多内建函数和方法的返回值存在,也就是说你必须对元组进行处理。只要不尝试修改元组,那么,“处理”元组在绝大多数情况下就是把它们当做列表来进行操作(除非需要使用一些元组没有的方法,例如`index`和`count`)。 一般来说,列表可能更能满足对序列的所有需求。 ## 2.5 小结 让我们回顾本章所涵盖的一些最重要的内容。 √ 序列。序列是一种数据结构,它包含的元素都进行了编号(从`0`开始)。典型的序列包括列表、字符串和元组。其中,列表是可变的(可以进行修改),而元组和字符串是不可变的(一旦创建了就是固定的)。通过分片操作可以访问序列的一部分,其中分片需要两个索引号来指出分片的起始和结束位置。要想改变列表,则要对相应的位置进行赋值,或者使用赋值语句重写整个分片。 √ 成员资格。`in`操作符可以检查一个值是否存在于序列(或者其他的容器)中。对字符串使用`in`操作符是一个特例,它可以查找子字符串。 √ 方法。一些内建类型(例如列表和字符串,元组则不在其中)具有很多有用的方法。这些方法有些像函数,不过它们与特定值联系得更密切。方法是面向对象编程的一个重要的概念,稍后的第七章中会对其进行讨论。 ### 2.5.1 本章的新函数 ``` cmp(x, y) 比较两个值 len(seq) 返回序列的长度 list(seq) 把序列转换成列表 max(args) 返回序列或者参数集合中的最大值 min(args) 返回序列或者参数集合中的最小值 reversed(seq) 对序列进行反向迭代 sorted(seq) 返回已排序的包含seq所有元素的列表 tuple(seq) 把序列转换成元组 ``` ### 2.5.2 接下来学什么 序列已经介绍完了,下一章会继续介绍由字符组成的序列,即_字符串_。