第11条 学会对序列做切片
Python有这样一种写法,可以从序列里面切割(slice)出一部分内容,让我们能够轻松地获取原序列的某个子集合。最简单的用法就是切割内置的list
、str
与bytes
。其实,凡是实现了__getitem__
与__setitem__
这两个特殊方法的类都可以切割(参见第43条)。
最基本的写法是用somelist[start:end]
这一形式来切割,也就是从start
开始一直取到end
这个位置,但不包含end
本身的元素。
如果是从头开始切割列表,那就应该省略冒号左侧的下标0,这样看起来更清晰。
如果一直取到列表末尾,那就应该省略冒号右侧的下标,因为用不着专门把它写出来。
用负数作下标表示从列表末尾往前算[1]。下面这些切割方式,即便是刚看到这段代码的人也应该能明白是什么意思。
这样写不会有什么奇怪的效果,大家尽管使用就好。
如果起点与终点所确定的范围超出了列表的边界,那么系统会自动忽略不存在的元素。利用这项特性,很容易就能构造出一个最多只有若干元素的输入序列,例如[2]:
切割时所用的下标可以越界,但是直接访问列表时却不行,那样会让程序抛出异常。
提示
用带负号的下标来切割列表,只有在个别情况下才会出现奇怪的效果。只要n
大于或等于1,somelist[-n:]
总是可以切割出你想要的切片。只有当n
为0的时候,才需要特别注意。此时somelist[-0:]
其实相当于somelist[0:]
,所以跟somelist[:]
一样,会制作出原列表的一份副本。
切割出来的列表是一份全新的列表。即便把某个元素换掉,也不会影响原列表中的相应位置。那个位置上的元素还是旧值。
切片可以出现在赋值符号的左侧,表示用右侧那些元素把原列表中位于这个范围之内的元素换掉。与unpacking形式的赋值不同,这种赋值不要求等号两边所指定的元素个数必须相同(如果是做unpacking,那么等号左侧用来接收数值的变量个数必须与等号右边所提供的数值个数一致,例如a, b = c[:2]
,参见第6条)。在原列表中,位于切片范围之前和之后的那些元素会予以保留,但是列表的长度可能有所变化。例如在下面这个例子中,列表会变短,因为赋值符号右侧只提供了3个值,但是左侧那个切片却涵盖了5个值,列表会比原来少两个元素。
下面这段代码会使列表变长,因为赋值符号右侧的元素数量比左侧那个切片所涵盖的元素数量要多。
起止位置都留空的切片,如果出现在赋值符号右侧,那么表示给这个列表做副本,这样制作出来的新列表内容和原列表相同,但身份不同。
把不带起止下标的切片放在赋值符号左边,表示是用右边那个列表的副本把左侧列表的全部内容替换掉(注意,左侧列表依然保持原来的身份,系统不会分配新的列表)。
要点
- 切片要尽可能写得简单一些:如果从头开始选取,就省略起始下标0;如果选到序列末尾,就省略终止下标。
- 切片允许起始下标或终止下标越界,所以很容易就能表达“取开头多少个元素”(例如
a[:20]
)或“取末尾多少个元素”(例如a[-20:0]
)等含义,而不用担心切片是否真有这么多元素。 - 把切片放在赋值符号的左侧可以将原列表中这段范围内的元素用赋值符号右侧的元素替换掉,但可能会改变原列表的长度。
[1]-k
,就是倒数第k
个位置。换算成不带负号的下标,就是len(a)-k
。——译者注
[2]first_twenty_items
是取a
的前20个元素,如果a
本身没有这么多元素,那就把所有元素全取出来。last_twenty_items
是取a
的最后20个元素,如果a
本身没有这么多元素,那同样把所有元素全取出来。——译者注