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

时间:2019-02-10 05:52:39

标签: python python-3.x sequence slice range-checking

因此,我刚刚遇到了我觉得像一个奇怪的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做的奇怪的事情吗?
-有目的吗?
-还是我在想这个错误的方式?

2 个答案:

答案 0 :(得分:75)

关于超范围索引的部分问题

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

为方便起见,允许切片索引扩展到结束点。必须对每个表达式进行范围检查,然后手动调整限制,这很痛苦,因此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")。请注意,目标“小”只有六个字母,“硕大的”只有九个字母。我们可以对列表做同样的事情:

>>> 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

希望这些实用示例很好地说明了切片为何如此工作。

答案 1 :(得分:22)

documentation给出了答案:

  

s[i:j]:从si的{​​{1}}的切片(注释(4))

     

(4)从js的{​​{1}}的切片被定义为项目序列   索引为i,这样j如果ki <= k < j大于    i,使用j 。如果省略len(s)len(s),请使用i。如果None   省略或0,请使用j。如果None大于或等于   len(s),切片为空。

documentation of IndexError确认了此行为:

  

例外i

     

在序列下标超出范围时引发。 (切片索引会被无提示地截断以落入允许范围内; 如果索引为   不是整数,则引发j。)

本质上,诸如IndexError之类的东西被简化为TypeErrorp[20:100]是列表末尾的空片,为其分配列表将修改列表末尾以包含所述列表。因此,它的工作原理类似于添加/扩展原始列表。

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

p[len(p):len(p]