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

Aka*_*ph7 72 python sequence slice python-3.x range-checking

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

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

p = [1,2,3]
p[3:] = [4] 
p = [1,2,3,4]
Run Code Online (Sandbox Code Playgroud)

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

p[20:22] = [5,6]
p = [1,2,3,4,5,6]
Run Code Online (Sandbox Code Playgroud)

更是如此:

p[20:100] = [7,8]
p = [1,2,3,4,5,6,7,8]
Run Code Online (Sandbox Code Playgroud)

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

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

Ray*_*ger 76

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

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

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

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

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

preview = msg[:50]
Run Code Online (Sandbox Code Playgroud)

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

n = len(msg)
preview = msg[:50] if n > 50 else msg
Run Code Online (Sandbox Code Playgroud)

手动实现调整终点的逻辑很容易忘记,容易出错(在两个地方更新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'
Run Code Online (Sandbox Code Playgroud)

这一切都归结为方便.

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

s[:] = []           # clear a list
t = u[:]            # copy a list
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • @Quuxplusone:Slice assignment*mutates*已经被`s`引用的列表; 使用`s =`*重新绑定*`s`来引用一个新列表.如果可以通过多个名称访问列表,并且您希望突变对所有名称可见,则切片分配就是您想要的.此外,如果`s`是全局的,重新分配`s`将需要一个`global`声明,但即使没有`global`语句,切片赋值也会产生类似的效果. (9认同)
  • "即使是现在,我们使用它来更新列表,当过滤_ [示例使用`s [:]`] _" - 你能否扩展为什么你使用`s [:] =`那里,而不只是`s = `?我从来没有见过任何人在诸如你在那里写的那一行的上下文中使用`s [:] =`.不错的答案! (2认同)

iz_*_*iz_ 23

文件有你的答案:

s[i:j]:的切片sij(注(4))

(4)的切片sij被定义为物品的索引序列k,使得i <= k < j.如果ij大于 len(s),请使用len(s).如果i省略或None使用0.如果j 省略或None使用len(s).如果i大于或等于 j,则切片为空.

确认此行为的文档IndexError:

例外 IndexError

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

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

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

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

In [2]: p[2:2] = [42, 42, 42]

In [3]: p
Out[3]: [1, 2, 42, 42, 42, 3, 4]
Run Code Online (Sandbox Code Playgroud)

  • 我不认为OP在询问切片是如何工作的,他要求设计选择背后的理由. (3认同)
  • @Primusa - 我相信他们会问_both_.这解释了_how_,这很有用,因为它解释了为什么行为没有被打破._why_可能埋藏在其中一个邮件列表的深处. (2认同)