在Python中,切片(slice)是对序列型对象(如list
, string
, tuple
)的一种高级索引方法。普通索引只取出序列中一个下标对应的元素,而切片取出序列中一个范围对应的元素,这里的范围不是狭义上的连续片段。
切片的基本语法为:
object[start_index : end_index : step]
如果没有缺省的话,表达式应该包含三个参数以及两个冒号,三个参数的意义分别如下:
1、start_index:切片的起始位置(包括该位置),0表示从第一个开始,1表示从第二个开始,以此类推。-1表示从倒数第一个开始,-2表示从倒数第二个开始,以此类推。缺省时取0或-1(即step为正数取0,负数取-1)
a=[9,2,8,7,4,5,1,5,6,8]
a[0::]表示从第一个到最后一个,结果为 #[9,2,8,7,4,5,1,5,6,8]
a[1::]表示从第二个到最后一个 #[2,8,7,4,5,1,5,6,8]
a[-1::]表示从最后一个到最后一个,一共取一个值 #[8]
2、end_index:切片的结束位置(!!!且不包括该位置),0表示第一个为终点,1表示第二个为终点,以此类推。-1表示倒数第一个为终点,-2表示倒数第二个为终点,以此类推。缺省时默认为序列长度(step为正数取正,step负数取负)
a=[9,2,8,7,4,5,1,5,6,8]
a[:0:]表示从第一个到第一个,一共取0个值 #[]
a[:1:]表示从第一个到第二个且不包括第二个 #[9]
a[:-1:]表示从第一个到最后一个且不包括最后一个,一共取9个值 #[9,2,8,7,4,5,1,5,6]
a[:8]表示从第一个到第九个且不包括第九个 #[9, 2, 8, 7, 4, 5, 1, 5]
3、step,表示步长。可取正负数,正数表示从左往右,负数表示从右往左。缺省时取1
a=[9,2,8,7,4,5,1,5,6,8]
a[::1]表示从第一个到最后一个,步长为1 #[9,2,8,7,4,5,1,5,6,8]
a[::-1]表示从最后一个到第一个,步长为1 #[9,6,5,1,5,4,7,8,2,9]
a[::2]表示从第一个到最后一个,步长为2 #[9,8,4,1,6]
只有一个冒号时默认step为1。当start_index的位置在end_index的左边时,表示从左往右取值;当start_index的位置在end_index的右边时,表示从右往左取值
>>> 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
,则基本索引的语法为a[index]
,其中index
为下标。读者可能会觉得这里过于简单,但我们要强调的是Python一个语法糖:负数下标索引,即:index
可以取为负数,当其为-n
时,对倒数第n
个元素进行索引。我们用一张表格值观展示a
的索引范围。
li = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(li[1])
# 2
print(li[9])
# 10
print(li[-5])
# 6
非负下标索引和负数下标索引共同构成了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]
可以使用这一特性倒置序列:
li = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(li[::-1])
# [10,9,8,7,6,5,4,3,2,1]
起始索引和结束索引正(+)负(-)混合索引的情况
li = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(li[1:-7:-1])
# []
'''
起始索引1在结束索引-7的左边,因此从左往右取值,但步长为-1则决定了从右往左取值,两者矛盾,因此为空。
'''
print(li[-1:7:-1])
# [10, 9]
# 起始索引-1在结束索引7的右边,因此从右往左取值,而步长-1同样决定了从右往左取值,因此结果正确
```
几种特殊情况:
a=[9,2,8,7,4,5,1,5,6,8]
a[1:6:-1] #step=-1表示从右往左取值,但是1:6表示从第二个到第七个从左往右,二者矛盾输出空列表
a[6:1] #step缺省时为1,但是6:1表示从第七个到第二个从右往左,二者矛盾,输出为空
a[-1:-6] #step缺省为1表示从左往右取值,但是-1:-6表示从倒数第一到倒数第六从右往左,矛盾输出为空
a[1:-6:-1] #step=-1表示从右往左,1:-6表示从左往右,矛盾 输出为空
a[-1:6] #step缺省为1从左到右,-1:6从右到左,两者矛盾,输出为空
li = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(li[:9][3:7][-1:])
# [7]
’‘’
相当于:
li[:9] =[1, 2, 3, 4, 5, 6, 7, 8, 9]
li[:9][3:7] =[4, 5, 6, 7]
li[:9][3:7][-1:]=[7]
理论上是可以无限的进行多层切片,只要上一次的切片结果不为空
‘’‘
l = [1,2,3,4,5,6,7,8]
l[:] # 取全部元素
[1, 2, 3, 4, 5, 6, 7, 8]
l[::-1] # 逆序
[8, 7, 6, 5, 4, 3, 2, 1]
l[::2] # 取下标为偶数位的元素
[1, 3, 5, 7]
l[1::2] # 下标为奇数位
[2, 4, 6, 8]
l[2:4] # 下标为 [2,4)
[3, 4]
l[:3] # 取前3位,下标为 [0,3)
[1, 2, 3]
l[:100] # 超过数组长度时,尾部截断
[1, 2, 3, 4, 5, 6, 7, 8]
l[100:0] # 起始位置大于长度时,返回空list
[]
l[-3:] # 取后3位,下标为 [n-3,n)
[6, 7, 8]
l[3:] # 下标为 [3,n)
[4, 5, 6, 7, 8]
l[:-3] # 下标为 [0, n-3)
[1, 2, 3, 4, 5]
l[3] # 取下标为3的数
4
l[-3] #l[n-3]=l[5] #取下标为n-3的数
6
l[-1] # 最后一个数,即下标为n-1的数
8
l[2::3] # 每隔3取一个数
[3, 6]
for i in range(3):
print(l[i:i+3]) # 窗口式读取,每次读3个数,循环3次
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
l[0] = 'a' #替换l[0]
l
['a', 2, 3, 4, 5, 6, 7, 8]
l[3:3] = 'c' # 在l[3]处插入元素
l
['a', 2, 3, 'c', 4, 5, 6, 7, 8]
l[:0]= 'a' # 头部插入
l
['a', 'a', 2, 3, 'c', 4, 5, 6, 7, 8]
l[len(l):0]= 'x' # 尾部插入
l
['a', 'a', 2, 3, 'c', 4, 5, 6, 7, 8, 'x']
l[:4] = ['e','f'] # 替换,前4位替换为2位
l
['e', 'f', 'c', 4, 5, 6, 7, 8, 'x']
l[-3:] = ['x','y','z'] # 最后3位替换为其他3位
l
['e', 'f', 'c', 4, 5, 6, 'x', 'y', 'z']
l[::2] = [0]*5 # 奇数为置0,间隔替换时,长度需要相等
l
[0, 'f', 0, 4, 0, 6, 0, 'y', 0]
li = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
li[2:6] = [6,6,6,6]
print(li)
# [1, 2, 6, 6, 6, 6, 7, 8, 9, 10]
a=[9,2,8,7,4,5,1,5,6,8]
print(f'{a=}')
#修改单个元素
a[3]='hello china' #序列变为[9, 2, 8, 'hello china', 4, 5, 1, 5, 6, 8]
print(f'修改了第3个元素,{a=}')
#插入某个元素
a[3:3]='A' #序列变为[9, 2, 8, 'A', 'hello china', 4, 5, 1, 5, 6, 8]
print(f'在第3个元素的地方插入了一个A,{a=}')
#替换一部分元素
a[3:5] = ['hello','world'] #序列变为[9, 2, 8, 'hello', 'world', 4, 5, 1, 5, 6, 8]
print(f'替换了第3、4元素,{a=}’)
‘’'
a=[9, 2, 8, 7, 4, 5, 1, 5, 6, 8]
修改了第3个元素,a=[9, 2, 8, 'hello china', 4, 5, 1, 5, 6, 8]
在第3个元素的地方插入了一个A,a=[9, 2, 8, 'A', 'hello china', 4, 5, 1, 5, 6, 8]
替换了第3、4元素,a=[9, 2, 8, 'hello', 'world', 4, 5, 1, 5, 6, 8]
‘''
l[:2] = [] #删除前2位元素
l
[0, 4, 0, 6, 0, 'y', 0]
del l[:2] # 删除前2位元素
l
[0, 6, 0, 'y', 0]
del l[::2] # 删除奇数位元素
l
[6, 'y']
li = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 我们可以通过切片来进行取出li中的奇数和偶数
# 奇数
print(li[::2])
# [1, 3, 5, 7, 9]
print(li[1::2])
# [2, 4, 6, 8, 10]