Python 为什么通过切片分配超过列表末尾的内容不会引发索引器?

Python 为什么通过切片分配超过列表末尾的内容不会引发索引器?,python,list,variable-assignment,slice,Python,List,Variable Assignment,Slice,我正在做一个最近通过slice实现的任务。这让我发现了Python内置的列表实现中的一些行为 给定一个空的列表和一个通过切片的赋值: >>> l = [] >>> l[100:] = ['foo'] 我希望这里有一个索引器来自列表,因为这种实现方式意味着无法从指定的索引中检索项目: >>> l[100] Traceback (most recent call last): File "<stdin>", line 1, in

我正在做一个最近通过slice实现的任务。这让我发现了Python内置的
列表
实现中的一些行为

给定一个空的
列表
和一个通过切片的赋值:

>>> l = []
>>> l[100:] = ['foo']
我希望这里有一个
索引器
来自
列表
,因为这种实现方式意味着无法从指定的索引中检索项目:

>>> l[100]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range
l[100:=['foo']
附加到
列表中(即,在此分配之后,
l==['foo']
),并且自那时起似乎一直以这种方式运行。我在任何地方都找不到该功能的文档(*),但CPython和PyPy的行为都是这样的

按索引分配会引发错误:

>>> l[100] = 'bar'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list assignment index out of range

(*)官方文件(谢谢)中对这种行为进行了定义,但没有解释为什么在派遣时会被认为是可取的



注意:我了解检索是如何工作的,并且可以看出在分配时与此保持一致可能是可取的,但我正在寻找一个引用的原因,说明为什么分配给切片会以这种方式进行
l[100:][/code>在
l[100:]['foo']
之后立即返回
l[100:]['foo']
但是
l[3:]['bar']
l[3:]['bar']
l[100:]['foo']
是令人惊讶的,特别是如果你是在Python之后。让我们看看到底发生了什么:

>>> l = []
>>> l[100:] = ['foo']
>>> l[100:]
[]
>>> l
['foo']
因此,分配实际上是成功的,并且该项作为第一项被放入列表中

之所以会发生这种情况,是因为索引位置的
100:
被转换为对象:
切片(100,无,无)

现在,
slice
类有一个方法
索引
(虽然我无法在线找到它的Python文档),当给定序列的长度时,该方法将根据该序列的长度调整
(开始、停止、跨步)

>>> slice(100, None, None).indices(0)
(0, 0, 1)
因此,当此切片应用于长度为0的序列时,它的行为与切片检索的切片
slice(0,0,1)
完全相同,例如,当
foo
为空序列时,它的行为就像
foo[0:0:1]一样,而不是
foo[100:][/code>抛出错误
已请求-这将导致检索时出现空切片

现在,当l是一个包含100多个元素的序列时,如果使用了
l[100://code>,setter代码应该可以正常工作。要使它在那里工作,最简单的方法是不要重新发明轮子,而只使用上面的
索引
机制。缺点是,它现在在边缘情况下看起来有点奇怪,但是对“超出边界”的切片的切片指定将被放置在当前序列的末尾。(然而,事实证明,CPython代码中很少有代码重用;实际上,它复制了所有这些索引处理,)

所以:若切片的开始索引大于或等于序列的长度,则生成的切片的行为就像它是从序列末尾开始的零宽度切片一样。即:如果
a>=len(l)
l[a:
的行为类似于内置类型上的
l[len(l):len(l)]
。这适用于分配、检索和删除


这样做的可取之处在于它不需要任何例外。
slice.indexes
方法不需要处理任何异常-对于长度为
l
的序列,
slice.indexes(l)
将始终产生
(开始、结束、跨步)
,这些索引可用于任何赋值、检索和删除,而且可以保证
start
end
都是
0索引,如果给定索引超出范围,则必须引发错误,因为无法返回可接受的默认值。(不能返回
None
,因为
None
可能是序列的有效元素)

相反,对于切片,如果任何索引超出范围,则不需要引发错误,因为可以将空序列作为默认值返回。这样做也是可取的,因为它提供了一种一致的方式来引用元素之间和序列末端之外的子序列(从而允许插入)

如中所述,如果切片的起始值或结束值大于
len(seq)
,则使用
len(seq)

因此,给定
a=[4,5,6]
,表达式
a[3://code>和
a[100://code>都指向列表中最后一个元素后面的空子序列。但是,在使用这些表达式分配切片后,它们可能不再引用相同的内容,因为列表的长度可能已更改


因此,在asignment
a[3:][7]
之后,片
a[3:][code>将返回
[7]
。但是在asignment
a[100:][8]
之后,片
a[100:][/code>仍将返回
[]
,因为
len(a)
仍然小于
100
。考虑到上面所述的一切,如果要保持切片分配和切片检索之间的一致性,这正是我们应该期望的。

a=l[100:]
不会导致错误,只是
a=[]
,合理的解释是,给定的
100
超出了
end
的范围,它只返回
end
。事实上,所有
start>stop
所在的片在
start
end
处返回一个空列表,该列表越小越好。@Johnsyweb。索引是指序列的特定元素,而切片是指序列的结构部分。在包含三个元素的列表中,切片
[1:1]
表示元素之间的空白部分。信息技术
>>> l = []
>>> l[100:] = ['foo']
>>> l[100:]
[]
>>> l
['foo']
>>> class Foo:
...     def __getitem__(self, i):
...         return i
... 
>>> Foo()[100:]
slice(100, None, None)
>>> slice(100, None, None).indices(0)
(0, 0, 1)