为什么Python允许序列的超出范围切片索引?

问题描述 投票:70回答:2

所以我只是遇到了一些奇怪的Python特性,并想要对它进行一些澄清。

以下数组操作有点有意义:

p = [1,2,3]
p[3:] = [4] 
p = [1,2,3,4]

我想它实际上只是将这个值附加到最后,对吗? 但是,我为什么要这样做呢?

p[20:22] = [5,6]
p = [1,2,3,4,5,6]

更是如此:

p[20:100] = [7,8]
p = [1,2,3,4,5,6,7,8]

这似乎是错误的逻辑。这似乎应该抛出错误!

任何解释? - 这只是Python的奇怪之处吗? - 有目的吗? - 我是否以错误的方式思考这个问题?

python python-3.x sequence slice range-checking
2个回答
77
投票

关于超出范围指数的部分问题

切片逻辑自动将索引剪切为序列的长度。

为方便起见,允许切片索引延伸超过端点。要对每个表达式进行范围检查然后手动调整限制将是一件痛苦的事情,因此Python会为您完成。

考虑想要显示不超过文本消息的前50个字符的用例。

简单的方法(Python现在做什么):

preview = msg[:50]

或者艰难的方式(自己做限制检查):

n = len(msg)
preview = msg[:50] if n > 50 else msg

手动实现调整终点的逻辑很容易忘记,容易出错(在两个地方更新50),会很啰嗦,而且会很慢。 Python将该逻辑移动到其内部,其中它是自动,快速和正确的。这是我喜欢Python的原因之一:-)

有关分配长度与输入长度不匹配的部分问题

OP还想知道允许赋值的基本原理,例如p[20:100] = [7,8],其中赋值目标的长度(80)与替换数据长度(2)不同。

通过类比字符串来看最简单的动机是最简单的。考虑一下,"five little monkeys".replace("little", "humongous")。请注意,目标“小”只有六个字母而“humongous”有九个字母。我们可以对列表做同样的事情:

>>> s = list("five little monkeys")
>>> i = s.index('l')
>>> n = len('little')
>>> s[i : i+n ] = list("humongous")
>>> ''.join(s)
'five humongous monkeys'

这一切都归结为方便。

在引入copy()和clear()方法之前,这些曾经是流行的习语:

s[:] = []           # clear a list
t = u[:]            # copy a list

即使是现在,我们在过滤时使用它来更新列表:

s[:] = [x for x in s if not math.isnan(x)]   # filter-out NaN values

希望这些实际的例子能够很好地理解为什么切片的工作原理。


23
投票

documentation有你的答案:

s[i:j]s切片到i(注释(4))

(4)从jsi切片被定义为具有j指数的项目序列,使得k。如果i <= k < ji大于j,请使用len(s)。如果省略len(s)i,请使用None。如果省略0j,请使用None。如果len(s)大于或等于i,则切片为空。

j证实了这种行为:

例外documentation of IndexError

当序列下标超出范围时引发。 (切片索引被静默截断以落在允许的范围内;如果索引不是整数,则会引发IndexError。)

基本上,像TypeError这样的东西正在减少到p[20:100]p[len(p):len(p]是列表末尾的空切片,为其分配列表将修改列表的末尾以包含所述列表。因此,它的作用就像追加/扩展原始列表一样。

此行为与将列表分配给原始列表中任何位置的空切片时的行为相同。例如:

p[len(p):len(p]
© www.soinside.com 2019 - 2024. All rights reserved.