a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
为方便总结,我们来分小类型举例切片的应用。
4.1. 切片表达式中仅有一对冒号情形
【提示】这种情况下,暗含着一个前提是:步长值step取值为1,因此切片方向遵循“自左向右”切的原则。
a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
4.1.1 最简单情形
a[2:6]
[2, 3, 4, 5]
a[2:9]
[2, 3, 4, 5, 6, 7, 8]
【提示】在此两例中,符合“左闭右开”原则,故最后切片结果集中去掉最后一个下标元素。
4.1.2 切片截止索引位置“超界”
根据上面切片规则7的说明,切片操作时索引不存在真正“越界”问题。请结合切片规则7看下面的举例。
a[2:10]
[2, 3, 4, 5, 6, 7, 8, 9]
【解析】根据“左闭右开”原则,切片结果集将包含a[2],a[3]……直到a[9]的8个元素。
a[2:20]
[2, 3, 4, 5, 6, 7, 8, 9]
【解析】根据切片规则7,a[2:20]等价于a[2:10](10=len(a),即列表最右边)。下面两个例子理由同这两个例子:
a[-3:10]
[7, 8, 9]
a[-3:20]
[7, 8, 9]
4.1.3 切片的起始索引位置超界
a[-10:-8]
[0, 1]
a[-100:-8]
[0, 1]
a[-100:2]
[0, 1]
a[-10:2]
[0, 1]
【解析】根据切片规则7,a[-100:-8]等价于a[-10:-8],a[-10:2] 等价于a[-100:2]。另一方面,a[-10:-8] 等价于a[0:-8](等价于a[0:2]),a[-10:2] 等价于a[0:2],故有上面运行结果。
4.1.4 切片起始索引或截止索引为负
a[-2:6]
[]
【解析】a[-2:6]等价于a[8:6],也等价于a[-2:6:1]和a[8:6:1],此时step=1(省略),易知切片结果集为空。
a[6:-2]
[6, 7]
【解析】a[6:-2]等价于a[6:8],也等价于a[6:-2:1]和a[6:8:1],此时step=1(省略),结合“左闭右开”原则,易知结果集对应[a[6],a[7]](而不包含a[8]),即切片结果集为[6, 7]。
4.1.5 切片规则2应用
a[6:2]
[]
a[-2:-6]
[]
【解析】符合切片规则2,易知切片结果集为空。
a[-6:-2]
[4, 5, 6, 7]
【解析】a[-6:-2] 等价于a[4:8],结合“左闭右开”原则,易知结果集对应[a[4],a[5],a[6],a[7]](而不包含a[8]),即切片结果集为[4,5,6, 7]。
4.1.6 省略切片起始索引时
a[:4]
[0, 1, 2, 3]
a[:-4]
[0, 1, 2, 3, 4, 5]
【解析】根据上面切片规则4.1,a[:4]等价于a[0:4],a[:-4] 等价于a[0:-4],而a[0:-4]又等价于a[0:6] ,结合“左闭右开”原则,易知有上面的切片结果集。
4.1.7 省略切片截止索引时
a[4:]
[4, 5, 6, 7, 8, 9]
a[-4:]
[6, 7, 8, 9]
【解析】根据上面切片规则5.1,a[4:]等价于a[4:10] (10=len(a)),a[-4:]等价于a[6:],而a[6:]等价于a[6:10] (10=len(a)),结合切片规则6“左闭右闭”原则(a[9]包含在结果集中),易知有上面的切片结果集。
4.1.8 切片起始索引和截止索引均省略时
a[:]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
【解析】根据上面切片规则4.1和5.1,a[:]等价于a[0:10](10=len(a)),而根据“左闭右开”原则,结果集中应当包含a[0],a[1],……a[9],故有上述切片结果集。
4.2. 切片表达式中有两对冒号情形
在切片表达式有两对冒号情况下,当step为1时(因为这种情况下可以省略第二对冒号,所以对应上面仅有一对冒号时),绝大部分情形我们已经讨论过。
【前提】本部分中,我们还是使用与上面同样结构与内容的列表a,如下所示:
a=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
4.2.1 step>1时
不失一般性,以下讨论不妨假设step=2。先看下面的例子。
a[2:7:2]
[2, 4, 6]
a[:3:2]
[0, 2]
a[:-3:2]
[0, 2, 4, 6]
a[3::2]
[3, 5, 7, 9]
a[-3::2]
[7, 9]
【解析】首先注意到,step=2决定了切片方向是自左向右。当step>1时,切片起始索引值默认为0。对于负数形式的起始还是截止索引值都可以转换成等价的正数形式索引值(例如a[:-3:2]等价于a[:7:2]),a[-3::2] 等价于a[7::2])来分析的。再结合“左闭右开”原则,这一组例题应该不难理解,故细节的解释在此省略。
4.2.2 step为负整数时
例1
a[8:3:-1]
[8, 7, 6, 5, 4]
【解析】首先注意到,step为-1决定了切片方向是自右向左。本例中,切片起始索引为8,终止索引为3,再结合切片“左闭右开”原则,故切片结果集中元素有[a[8],a[7],a[6],a[5],a[4]],即[8,7,6,5,4]。
例2
a[10:0:-2]
[9, 7, 5, 3, 1]
【解析】首先注意到,step为-2决定了切片方向是自右向左。其次,根据上面切片原则7,切片的时候不存在索引越界情况,a[10]不存在,则继续往内分析,a[9]=9。于是,结合切片“左闭右开”原则和步长(即跨度)为2,故切片结果集中元素有[a[9],a[7],a[5],a[3],a[1]],即[9,7,5,3,1]。
例3
a[0:10:-2]
[]
【解析】首先注意到,step为-2决定了切片方向是自右向左。而切片初始索引为0,即列表的左边界,此时再向左切肯定没有元素可切了。因此,根据切片规则3,结果为空列表。
例4
a[:4:-1]
[9, 8, 7, 6, 5]
【解析】首先注意到,step为-1决定了切片方向是自右向左。此时,切片初始索引对应列表的最右边(=len(列表)),即有切片从索引10(实际从索引9开始,包含该索引)开始沿着自右向左的方向切片,直到索引4(不包含,依据是“左闭右开”),故有上述切片结果。
例5
a[::-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
【解析】首先注意到,step为-1决定了切片方向是自右向左。那么,接下来最关键的问题是搞清:在step为负数且切片初始索引和切片终止索引没有提供的情况下,这个切片初始索引和切片终止索引的值为多少。结合上面规则4.2和5.2,初始索引对应列表的最右边(=len(列表)),切片的截至索引位置为列表的最左边的左侧(即参与切片的子集中包含索引为0的元素)。当然,也可以简单根据上面规则6确定出最终的切片结果为列表中原来所有元素的倒序。
例6
a[5::-2]
[5, 3, 1]
【解析】首先注意到,step为-2决定了切片方向是自右向左。切片初始索引为5,结果集中自然包含a[5]。根据上面分析,本例中切片的截至索引位置为列表的最左边的左侧(即参与切片的子集中包含索引为0的元素)。从等价关系来分析的话,a[5::-2]等价于a[5:-11:-2](下标-11对应着下标-10再往左,类似于下标9再往右对应的下标10)。所以,有此例中运行结果。
思考题
a[0::-1]的结果是多少?
a[:-3:2] 的结果是多少?
a[1:6:-1] 的结果是多少?
a[-6::-1] 的结果是多少?
连续切片操作:a[:8][2:5][-1:] 的结果是多少?
a[2+1:3*2:7%3] 的结果是多少?
下列代码片断的运行结果是什么?
for i in range(1,100)[2::3][-10:]:
print(i)
【提示】利用range函数生成1-99的整数,然后取3的倍数,再取最后十个。
小结
理论上而言,只要条件表达式得当,可以通过单次或多次切片操作实现任意切取目标值。初看上去,切片操作的基本语法比较简单,但是深挖起来,并不简单。因此,如果不彻底搞清楚内在逻辑,也极容易产生错误,而且这种错误有时隐蔽得比较深,难以察觉。
作为补充,本文中提到的“左闭右开”原则,更细致地说法是“开始闭结束开”原则。即是说,开始索引对应元素参与切片运算,而结束索引对应元素并不参与切片运算。另外,步长值为负数,并且在省略切片起始索引或者切片终止索引情况下,这两种索引的默认值应当结合“切片不存在索引越界”原则进行正确理解。本文通过详细例子总结归纳了切片操作的各种情况。若有错误和不足之处请各位指正!