6.1 序列

序列类型有着相同的访问模式:它的每一个元素可以通过指定一个偏移量的方式得到。而多个元素可以通过切片操作的方式一次得到,切片操作会在接下来的内容中讲到。下标偏移量是从0开始到总元素数-1结束——之所以要减1是因为我们是从0开始计数的。图6-1阐述了序列的元素是如何存储的。

6.1.1 标准类型操作符

标准类型操作符(参见4.5节)一般都能适用于所有的序列类型。当然,如果作复合类型的对象比较的话,这样说可能需要有所保留,不过其他的操作绝对是完全适用的。

6.1.2 序列类型操作符

表6.1列出了对所有序列类型都适用的操作符。操作符是按照优先级从高到底的顺序排列的。

1. 成员关系操作符(in、not in)

成员关系操作符是用来判断一个元素是否属于一个序列的。比如对字符串类型来说,就是判断一个字符是否属于这个字符串;对和元组类型来说,就代表了一个对象是否属于该对象序列。in/not in操作符的返回值一般来讲就是True/False,满足成员关系就返回True,否则返回False。该操作符的语法如下。

6.1 序列 - 图1

6.1 序列 - 图2

2. 连接操作符(+)

这个操作符允许我们把一个序列和另一个相同类型的序列做连接。语法如下。

6.1 序列 - 图3

该表达式的结果是一个包含sequence1和sequence2的内容的新序列。注意,这种方式看起来似乎实现了把两个序列内容合并的概念,但是这个操作不是最快或者说最有效的。

对字符串来说,这个操作不如把所有的子字符串放到一个列表或可迭代对象中,然后调用一个join方法来把所有的内容连接在一起节约内存;类似地,对列表来说,我们推荐读者用列表类型的extend()方法来把两个或者多个列表对象合并。当你需要简单地把两个对象的内容合并,或者说不能依赖于可变对象的那些没有返回值(实际上它返回一个None)的内建方法来完成的时候时,连接操作符还是很方便的一个选择。下面的切片操作可以视作这些情况的例子。

3. 重复操作符(*)

当你需要需要一个序列的多份拷贝时,重复操作符非常有用,它的语法如下。

6.1 序列 - 图4

copies_int必须是一个整型(1.6节里面有讲到,不能是长整型)。像连接操作符一样,该操作符返回一个新的包含多份原对象拷贝的对象。

4. 切片操作符([],[:],[::])

简单地讲,所谓序列类型就是包含一些顺序排列的对象的一个结构。你可以简单的用方括号加一个下标的方式访问它的每一个元素,或者通过在方括号中用冒号把开始下标和结束下标分开的方式来访问一组连续的元素。

下面我们将详细的讲解提到的这两种方式。序列类型是其元素被顺序放置的一种数据结构类型,这种方式允许通过指定下标的方式来获得某一个数据元素,或者通过指定下标范围来获得一组序列的元素。这种访问序列的方式叫做切片,我们通过切片操作符就可以实现我们上面说到的操作。

访问某一个数据元素的语法如下:

6.1 序列 - 图5

sequence是序列的名字,index是想要访问的元素对应的偏移量。偏移量可以是正值。范围从0到偏移最大值(比序列长度少一),用len()函数(下一节会讲),可以得到序列长度,实际的范围是0<=index<=len(sequence)-1。

另外,也可以使用负索引,范围是-1到序列的负长度,-len(sequence),-len(sequence) <=index<=-1。正负索引的区别在于正索引以序列的开始为起点,负索引以序列的结束为起点。

试图访问一个越界的索引会引发一个如下的异常;

6.1 序列 - 图6

因为Python是面向对象的,所以你可以像下面这样直接访问一个序列的元素(不用先把它赋值给一个变量)。

6.1 序列 - 图7

这个特性在你调用一个返回值是序列类型的函数,并且你只对返回的序列中的一个或某几个元素感兴趣时特别有用。

那么我们如何才能一次得到多个元素呢?其实这跟访问某一个单一元素一样简单,只要简单的给出开始和结束的索引值,并且用冒号分隔就可以了,其语法如下。

6.1 序列 - 图8

通过这种方式我们可以得到从起始索引到结束索引(不包括结束索引对应的元素)之间的一“片”元素。起始索引和结束索引都是可选的,如果没有提供或者用None作为索引值,切片操作会从序列的最开始处开始,或者直到序列的最末尾结束。

在图6-2~图6-6里面,我们以一个长度为5的序列为例,分别讲解了这几种切片方式。

6.1 序列 - 图9

图 6-2 整个序列:sequence或sequence[:]

6.1 序列 - 图10

图 6-3 序列切片操作:sequence[0:3]或sequence[:3]

6.1 序列 - 图11

图 6-4 序列切片操作:sequence[2:5]或sequence[2:]

6.1 序列 - 图12

图 6-5 序列切片操作:sequence[1:3]

6.1 序列 - 图13

图 6-6 序列切片操作:sequence[3]

5. 用步长索引来进行扩展的切片操作

序列的最后一个切片操作是扩展切片操作,它多出来的第三个索引值被用做步长参数。你可以把这个参数看成跟内建函数range()里面的步长参数或者类似于C/C++、Perl、PHP和Java语言里面for语句中的步长参数一样来理解。

Python的虚拟机里面其实很早就有了扩展切片操作,只不过以前需要通过扩展的方式来使用。Jython也支持这个语法(以前叫JPython)。

很久以前的C解释程序2.3版就给出了其他所有方法。

以下是几个例子。

6.1 序列 - 图14

6. 切片索引的更多内容

切片索引的语法要比简单的单一元素索引灵活得多。开始和结束素引值可以超过字符串的长度。换句话说,起始索引可以小于0,而对于结束索引,即使索引值为100的元素并不存在也不会报错,简单地说,即使用100来作为一个长度不到100的序列的结束索引也不会有什么问题,例子如下。

6.1 序列 - 图15

有这么一个问题:有一个字符串,我们想通过一个循环按照这样的形式显示它:每次都把位于最后的一个字符砍掉,下面是实现这个要求的一种方法。

6.1 序列 - 图16

可是,该如何在第一次迭代的时候显示整个字符串呢?是否有一种方法可以不用在整个循环之前加入一个额外的print语句呢?我们该如何定义一个索引,来代表整个的序列呢?事实上在一个以负数作为索引的例子里是没有一个真正能解决这个问题的方法的,因为-1已经是“最小”的索引了。我们不可能用0来作为索引值,因为这会切片到第一个元素之前而什么都不会显示。

6.1 序列 - 图17

我们的方案是使用另一个小技巧:用None作为索引值,这样一来就可以满足你的需要,比如说,在你想用一个变量作为索引来从第一个到遍历最后一个元素的时候:

6.1 序列 - 图18

现在这个程序符合我们的要求了。在进行下面的内容之前,必须指出,似乎还可以先创建一个只包含None的列表,然后用extend()函数把range()的输出添加到这个列表,或者先建立range()输出组成的列表然后再把None插入到这个列表的最前面,然后对这个列表进行遍历,但是可变对象的内建函数extend()根本就没有返回值,所以这个方法是行不通的。

6.1 序列 - 图19

这个错误发生的原因是[None].extend(…)函数返回None,None既不是序列类型也不是可迭代对象。在这种情况下使用上面提到的列表连接操作来实现是唯一不需要添加额外代码的方法。

6.1.3 内建函数(BIF)

在讲解序列类型的内建函数之前,有一点需要说明,序列本身就内含了迭代的概念,之所以会这样,是因为迭代这个概念就是从序列,迭代器,或者其他支持迭代操作的对象中泛化得来的。

由于Python的for循环可以遍历所有的可迭代类型,在(非纯序列对象上)执行for循环时就像在一个纯序列对象上执行一样。而且Python的很多原来只支持序列作为参数的内建函数现在也开始支持迭代器或者或类迭代器了。我们把这些类型统称为“可迭代对象”。

在这一章里我们会详细的讨论跟序列关系紧密的内建函数(BIF)。在第八章“条件判断和循环”里面将讨论针对“在循环中迭代”这种情况的内建函数(BIF)。

1. 类型转换

内建函数list()、str()和tuple()被用做在各种序列类型之间转换。你可以把它们理解成其他语言里面的类型转换,但是并没有进行任何的转换。这些转换实际上是工厂函数(在第4章介绍),将对象作为参数,并将其内容(浅)拷贝到新生成的对象中。表6.2列出了适用于序列类型的转换函数。

6.1 序列 - 图20

我们又用了一次“转换”这个词。不过,为什么Python里面不简单地把一个对象转换成另一个对象呢?回过头看一下第4章就会知道,一旦一个Python的对象被建立,我们就不能更改其身份或类型了。如果你把一个列表对象传给list()函数,便会创建这个对象的一个浅拷贝,然后将其插入新的列表中。同样地,在做连接操作和重复操作时,我们也会这样处理。

所谓浅拷贝就是只拷贝了对对象的索引,而不是重新建立了一个对象。如果你想完全的拷贝一个对象(包括递归,如果你的对象是一个包含在容器中的容器),你需要用到深拷贝,关于浅拷贝和深拷贝的更多信息会在本章的末尾讲到。

str()函数在需要把一个对象的可打印信息输出时特别有用,不仅仅是对序列类型,对其他类型的对象同样如此。Unicode()是str()函数的unicode版本,它跟str()函数基本一样。list()和tuple()函数在列表类型和元组类型的互换时非常有用。不过,虽然这些函数也适用于string类型(因为string类型也是序列的一种),但是在string类型上应用tupleO和list()函数却得不到我们通常希望的结果。

2. 可操作

Python为序列类型提供以下可操作BIF(见表6.3)注意,len()、reversed()和sum()函数只能接受序列类型对象作为参数,而剩下的则还可以接受可迭代对象作为参数,另外,max()和min()函数也可以接受一个参数列表。

6.1 序列 - 图21

我们将分别在每个序列的章节里面提供使用这些函数的例子。