当我们根据单个索引进行取值时,如果索引越界,就会得到报错:“IndexError: list index out of range”。
- >>> li = [1, 2]
- >>> li[5]
- Traceback (most recent call last):
- File "
" , line 1, in - IndexError: list index out of range
对于一个非空的序列对象,假设其长度为 length,则它有效的索引值是从 0 到(length - 1)。如果把负数索引也考虑进去,则单个索引值的有效区间是 -length, length - 1 闭区间。
但是,当 Python 切片中的索引超出这个范围时,程序并不会报错。
- >>> li = [1, 2]
- >>> li[1:5] # 右索引超出
- [2]
- >>> li[5:6] # 左右索引都超出
- []
其实,对于这种现象,官方文档中有所介绍:
The slice of s from i to j is defined as the sequence of items with index k such that
i <= k < j
. If i or j is greater thanlen(s)
, uselen(s)
. If i is omitted orNone
, use0
. If j is omitted orNone
, uselen(s)
. If i is greater than or equal to j, the slice is empty.
也就是说:
对照上面的例子,可以得到:
- >>> li = [1, 2]
- >>> li[1:5] # 等价于 li[1:2]
- [2]
- >>> li[5:6] # 等价于 li[2:2]
- []
归结起来一句话:Python 解释器把可能导致索引越界的操作给屏蔽了,你的写法可以很自由,但是最终的结果会被死死限制在合法的索引区间内。
对于这个现象,我其实是有点疑惑的,为什么 Python 不直接报索引越界呢,为什么要修正切片的边界值,为什么一定要返回一个值呢,即便这个值可能是个空序列?
当我们使用“li5:6”时,至少在字面意义上想表达的是“取出索引从 5 到 6 所对应的值”,就像是在说“取出书架上从左往右数的第 6 和 7 本书”。
如果程序是如实地遵照我们的指令的话,它就应该报错,就应该说:对不起,书架上的书不够数。
实话说,我并没有查到这方面的解释,这篇文章也不是要给大家科普 Python 在设计上有什么独到的见解。恰恰相反,这篇文章的主要目的之一是希望得到大家的回复解答。
在 Go 语言中,遇到同样的场景时,它的做法是报错“runtime error: slice bounds out of range”。
在 Rust 语言中,遇到同样的场景时,它的做法是报错“byte index 5 is out of bounds of ......”。
在其它支持切片语法的语言中,也许还有跟 Python 一样的设计。但是,我还不知道有没有(学识浅薄)……
最后,继续回到标题中的问题“Python 的切片为什么不会索引越界”。我其实想问的问题有两个:
对于第一个问题的回答,官方文档已经写得很明白了。
对于第二个问题,本文暂时没有答案。