在Python中,切片(slice)是对序列型对象(如list
, string
, tuple
)的一种高级索引方法。普通索引只取出序列中一个下标对应的元素,而切片取出序列中一个范围对应的元素,这里的范围不是狭义上的连续片段。下面的代码初步展示了切片索引的力量。
>>> a = list(range(10))
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[:5]
[0, 1, 2, 3, 4]
>>> a[5:]
[5, 6, 7, 8, 9]
>>> a[2:8]
[2, 3, 4, 5, 6, 7]
>>> a[::2]
[0, 2, 4, 6, 8]
>>> a[::-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
你可能还看不懂具体的语法(这正是本篇要介绍的),但你应该已经领略到了切片的花式操作。
写这篇文章的初衷是,一方面切片操作是Python中非常常见的,另一方面网上却很难找到全面系统的解析,比如以下结果是否让人有些迷惑:
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[0::-1]
[0]
>>> a[0:len(a):-1]
[]
为什么有len(a)
和省略len(a)
结果会不一样?
本文致力于真正讲清楚Python切片的使用方法。若您希望进一步了解切片的实现原理,以帮助我们自定义类的切片操作,敬请期待本文的续篇,《切片完全指南(原理篇)》。
我们从Python的基本索引开始,即单个整数的索引。假设被索引的序列仍为之前提到的a
,则基本索引的语法为a[index]
,其中index
为下标。读者可能会觉得这里过于简单,但我们要强调的是Python一个语法糖:负数下标索引,即:index
可以取为负数,当其为-n
时,对倒数第n
个元素进行索引。我们用一张表格值观展示a
的索引范围。
非负下标索引和负数下标索引共同构成了Python索引的有效范围:。有效范围的概念对切片的理解非常重要,在基本索引中,索引超出有效范围时会抛出IndexError
异常:
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[10]
Traceback (most recent call last):
File "", line 1, in
IndexError: list index out of range
>>> a[-11]
Traceback (most recent call last):
File "", line 1, in
IndexError: list index out of range
但在切片中不是这样,我们将在下文具体解释。
简单切片指的是这样的切片形式:a[start:stop]
,其行为是得到下标在这样一个前闭后开区间范围内的元素,其中start
和stop
为负数时,简单看作是负数下标对应的位置即可:
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[2:3]
[2]
>>> a[5:9]
[5, 6, 7, 8]
>>> a[5:-1]
[5, 6, 7, 8]
>>> a[-5:9]
[5, 6, 7, 8]
>>> a[-5:-1]
[5, 6, 7, 8]
事情到这里也很简单,下面着重讲解两个比较特殊的情况:超出有效索引范围和缺省。
当start
或stop
超出上文提到的有效索引范围时,切片操作不会抛出异常,而是进行截断。可以这样去理解截断机制:我们假象把索引范围扩充到全体整数,只不过小于或大于的区域对应空元素,在这个扩充后的数轴上进行切片,只需把最终结果中的所有空元素忽略即可。
来看几个具体的例子
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[-100:5]
[0, 1, 2, 3, 4]
>>> a[5:100]
[5, 6, 7, 8, 9]
>>> a[-100:100]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[100:1000]
[]
另外,如果start
的位置比stop
还靠后怎么办?Python还是不会抛出异常,而是直接返回空序列:
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[6:5]
[]
start
和stop
都是可以缺省的,在缺省的情况下,Python的行为是尽可能取最大区间,具体来说:
按照扩充索引范围的观点,start
的缺省值是 无穷小(),stop
的缺省值是 无穷大()。
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[:5]
[0, 1, 2, 3, 4]
>>> a[5:]
[5, 6, 7, 8, 9]
>>> a[100:]
[]
早期的Python解释器仅支持上述a[start:stop]
形式的基本切片,后来加入了下面要介绍的切片形式,扩展切片的名称也流传下来,实际上不用担心,这早已是Python所支持的标准语法。
扩展切片指的是这样的切片形式:a[start:stop:step]
,其中step
是一个非零整数,即比简单切片多了调整步长的功能,此时切片的行为可概括为:从start
对应的位置出发,以step
为步长索引序列,直至越过stop
对应的位置,且不包括stop
本身。事实上,简单切片就是step=1
的扩展切片的特殊情况。需要详细解释的是step
分别为正数和负数的两种情况。
step
为正数当step
为正数时,切片行为很容易理解,start
和stop
的截断和缺省规则也与简单切片完全一致:
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[0:6:2]
[0, 2, 4]
>>> a[::2]
[0, 2, 4, 6, 8]
>>> a[:-2:2]
[0, 2, 4, 6]
>>> a[4::2]
[4, 6, 8]
step
为负数当step
为负数时,切片将其解释为从start
出发以步长|step|
逆序索引序列,此时,start
和stop
的截断依然遵循前述规则,但缺省发生一点变化,因为我们说过,在缺省的情况下,Python的行为是尽可能取最大区间,此时访问是逆序的,start
应尽量取大,stop
应尽量取小,才能保证区间最大,因此:
按照扩充索引范围的观点,start
的缺省值是 无穷大(),stop
的缺省值是 无穷小()
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a[5::-1]
[5, 4, 3, 2, 1, 0]
>>> a[:4:-2]
[9, 7, 5]
>>> a[::-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]