乐趣区

关于python:Python-的切片为什么不会索引越界

切片(slice)是 Python 中一种很有特色的个性,在正式开始之前,咱们先来温习一下对于切片的常识吧。

切片次要用于序列对象中,依照索引区间截取出一段索引的内容。

切片的书写模式:[i : i+n : m];其中,i 是切片的起始索引值,为列表首位时可省略;i+n 是切片的完结地位,为列表末位时可省略;m 能够不提供,默认值是 1,不容许为 0,当 m 为正数时,列表翻转。

切片的根本含意是: 从序列的第 i 位索引起,向右取到后 n 位元素为止,按 m 距离过滤

上面是一些很有代表性的例子,根本涵盖了切片语法的应用要点:

# @Python 猫
li = [1, 4, 5, 6, 7, 9, 11, 14, 16]

# 以下写法都能够示意整个列表,其中 X >= len(li)
li[0:X] == li[0:] == li[:X] == li[:] == li[::] == li[-X:X] == li[-X:]

li[1:5] == [4,5,6,7] # 从 1 起,取 5 - 1 位元素
li[1:5:2] == [4,6] # 从 1 起,取 5 - 1 位元素,按 2 距离过滤
li[-1:] == [16] # 取倒数第一个元素
li[-4:-2] == [9, 11] # 从倒数第四起,取 -2-(-4)= 2 位元素
li[:-2] == li[-len(li):-2] == [1,4,5,6,7,9,11] # 从头开始,取 -2-(-len(li))= 7 位元素

# 步长为正数时,列表先翻转,再截取
li[::-1] == [16,14,11,9,7,6,5,4,1] # 翻转整个列表
li[::-2] == [16,11,7,5,1] # 翻转整个列表,再按 2 距离过滤
li[:-5:-1] == [16,14,11,9] # 翻转整个列表,取 -5-(-len(li))= 4 位元素
li[:-5:-3] == [16,9] # 翻转整个列表,取 -5-(-len(li))= 4 位元素,再按 3 距离过滤

# 切片的步长不能够为 0
li[::0]  # 报错(ValueError: slice step cannot be zero)

像 C/C++、Java 和 JavaScript 等语言,尽管也反对某些“切片”性能,例如截取数组或字符串的片段,然而,它们并没有一种在语法层面上的通用性反对。

依据维基百科材料,Fortran 是最早反对切片语法的语言(1966),而 Python 则是最具代表性的语言之一。

另外,像 Perl、Ruby、Go 和 Rust 等语言,尽管也有切片,但都不迭 Python 那样灵便和自在(因为它反对 step、正数索引、缺省索引)。

切片的根本用法就可能满足大部分的需要,然而,Python 切片还有一些进阶的用法,例如:切片占位符用法(可实现列表的赋值、删除与拼接操作)、自定义对象实现切片性能、迭代器切片(itertools.islice())、文件对象切片等等。关联浏览:Python 进阶:全面解读高级个性之切片!

对于切片的介绍与复习,就到这里了。

上面进入文章题目的问题:Python 的切片语法为什么不会呈现索引越界呢?

当咱们依据单个索引进行取值时,如果索引越界,就会失去报错:“IndexError: list index out of range”。

>>> li = [1, 2]
>>> li[5]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
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 than len(s), use len(s). If i is omitted or None, use 0. If j is omitted or None, use len(s). If i is greater than or equal to j, the slice is empty.

也就是说:

  • 当左或右索引值大于序列的长度值时,就用长度值作为该索引值;
  • 当左索引值缺省或者为 None 时,就用 0 作为左索引值;
  • 当右索引值缺省或者为 None 时,就用序列长度值作为右索引值;
  • 当左索引值大于等于右索引值时,切片后果为空对象。

对照下面的例子,能够失去:

>>> li = [1, 2]
>>> li[1:5]  # 等价于 li[1:2]
[2]
>>> li[5:6]  # 等价于 li[2:2]
[]

归纳起来一句话:Python 解释器把可能导致索引越界的操作给屏蔽了,你的写法能够很自在,然而最终的后果会被死死限度在非法的索引区间内。

对于这个景象,我其实是有点纳闷的,为什么 Python 不间接报索引越界呢,为什么要修改切片的边界值,为什么肯定要返回一个值呢,即使这个值可能是个空序列?

当咱们应用“li[5: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 的切片为什么不会索引越界”。我其实想问的问题有两个:

  • 当切片语法中的索引超出边界时,为什么 Python 还能返回后果,返回后果的计算原理是什么?
  • 为什么 Python 的切片语法要容许索引超出边界呢,为什么不设计成抛出索引谬误?

对于第一个问题的答复,官网文档曾经写得很明确了。

对于第二个问题,本文临时没有答案。

兴许我很快就能找到答案,然而,也可能须要很久。不论如何,本文先到此为止了。

如果你喜爱钻研 Python 设计上的小细节,感兴趣探究“为什么”问题的解答,欢送关注“Python 为什么”系列文章。

举荐浏览最受大家喜爱的往期话题:

(1)Python 为什么举荐蛇形命名法?

(2)Python 为什么用 # 号作正文符?

(3)Python 之父为什么厌弃 lambda 匿名函数?

(4)Python 为什么不反对 switch 语句?

(5)[Python 疑难问题:[] 与 list() 哪个快?为什么快?快多少呢?](https://mp.weixin.qq.com/s/-y…)

(6)Python 为什么不反对 i++ 自增语法,不提供 ++ 操作符?

本文属于“Python 为什么”系列(Python 猫出品),该系列次要关注 Python 的语法、设计和倒退等话题,以一个个“为什么”式的问题为切入点,试着展示 Python 的迷人魅力。所有文章将会归档在 Github 上,我的项目地址:https://github.com/chinesehua…

退出移动版