为什么Python 3中的切片仍然是副本而不是视图?

JAB*_*JAB 50 python language-design slice python-3.x

正如我在评论这个答案后才注意到的那样,Python 3中的切片返回了他们正在切片而不是视图的浅层副本.为什么仍然如此?即使撇开观点numpy的的使用,而不是切片的副本,事实证明dict.keys,dict.valuesdict.items在Python 3所有回报的意见,并有走向更大的使用迭代器面向Python 3中的其他许多方面,使它看起来将会有些切片变得相似的运动.itertools确实有一个islice函数可以进行迭代切片,但是它比普通切片更有限,并且不提供沿着dict.keys或者行的视图功能dict.values.

同样,你可以使用赋值给切片来修改原始列表,但切片本身就是副本而不是视图,这是语言的一个矛盾方面,似乎它违反了Python的Zen中所说明的几个原则.

也就是说,你可以做的事实

>>> a = [1, 2, 3, 4, 5]
>>> a[::2] = [0, 0, 0]
>>> a
[0, 2, 0, 4, 0]
Run Code Online (Sandbox Code Playgroud)

但不是

>>> a = [1, 2, 3, 4, 5]
>>> a[::2][0] = 0
>>> a
[0, 2, 3, 4, 5]
Run Code Online (Sandbox Code Playgroud)

或类似的东西

>>> a = [1, 2, 3, 4, 5]
>>> b = a[::2]
>>> b
view(a[::2] -> [1, 3, 5])   # numpy doesn't explicitly state that its slices are views, but it would probably be a good idea to do it in some way for regular Python
>>> b[0] = 0
>>> b
view(a[::2] -> [0, 3, 5])
>>> a
[0, 2, 3, 4, 5]
Run Code Online (Sandbox Code Playgroud)

似乎有点武断/不受欢迎.

我知道http://www.python.org/dev/peps/pep-3099/以及它所说的"切片和扩展切片不会消失的部分(即使可以替换API __getslice____setslice__API)"他们是否会返回标准对象类型的视图."但是链接的讨论没有提到为什么做出关于切片视图的决定; 事实上,原始帖子中列出的建议中对该特定建议的大多数评论似乎都是积极的.

是什么阻止了这样的东西在Python 3.0中实现,Python 3.0专门设计为不与Python 2.x严格向后兼容,因此可能是实现这种设计变更的最佳时机,并且有什么可能的在未来的Python版本中阻止它?

Sin*_*ion 11

同样,您可以使用赋值给切片来修改原始列表,但切片本身就是副本而不是视图.

嗯..那不太对; 虽然我可以看到你怎么想.在其他语言中,切片分配,例如:

a[b:c] = d
Run Code Online (Sandbox Code Playgroud)

相当于

tmp = a.operator[](slice(b, c)) # which returns some sort of reference
tmp.operator=(d)        # which has a special meaning for the reference type.
Run Code Online (Sandbox Code Playgroud)

但在python中,第一个语句实际上转换为:

a.__setitem__(slice(b, c), d)
Run Code Online (Sandbox Code Playgroud)

也就是说,项目赋值实际上是在python中特别识别的,具有特殊含义,与项目查找和赋值分开; 他们可能是无关的.这与python作为一个整体是一致的,因为python没有像C/C++中的"lvalues"那样的概念; 没有办法重载赋值运算符本身; 仅当赋值的左侧不是普通标识符时的特定情况.

假设列表确实有视图; 你试图使用它:

myView = myList[1:10]
yourList = [1, 2, 3, 4]
myView = yourList
Run Code Online (Sandbox Code Playgroud)

在python之外的语言中,可能有一种方法可以yourList推进myList,但在python中,由于名称myView显示为一个简单的标识符,它只能表示一个变量的assignemnt; 视图丢失了.

  • 至少Numpy通过使用`myView [:] = yourList`来解释裸标识符位.同样的事情可以在常规Python中完成.`一个[:]`,`为语法糖一.__的GetItem __(片(无))`,将在`A`返回的所有项目的视图(或由`A`引用在底层对象中的项如果一个` `本身就是一个视图)而不是默认的`a`的浅层副本.这本身实际上可能是稍微unpythonic,但它似乎并不像它会变得比什么已经做了这么多. (5认同)
  • 因为最初的意图是用于与Python的内置类型更好地融合的东西.并且如我自己的回答所示,虽然使用numpy确实看起来确实是这种情况下的最佳解决方案,但它仍然不是最优的,除非存在通过切片扩展ndarray的相对简单的方法,使得对象保持不变,我不知道的. (3认同)
  • 如果你喜欢numpy那么多..为什么不只是使用numpy?`np.array((),np.object_)` (2认同)

JAB*_*JAB 6

嗯,看来我发现了视图决定背后的很多推理,通过以http://mail.python.org/pipermail/python-3000/2006-August/003224.html开始的线程(它主要是关于切片字符串,但线程中至少有一封电子邮件提到了可变对象(例如列表),以及来自以下内容的一些内容:

http://mail.python.org/pipermail/python-3000/2007-February/005739.html
http://mail.python.org/pipermail/python-dev/2008-May/079692.html和以下电子邮件线程中的邮件

看起来,对于基础 Python 来说,切换到这种风格的优势将被引发的复杂性和各种不良的边缘情况所抵消。那好吧。

...然后,当我开始想知道是否有可能将当前的slice对象处理方式替换为可迭代形式 la itertools.islice,就像zipmap等所有返回可迭代对象而不是 Python 3 中的列表一样,我开始意识到所有意外的行为以及由此可能产生的问题。看来目前这可能是一个死胡同。

从好的方面来说,numpy 的数组相当灵活,因此在可能需要这种事情的情况下,使用一维 ndarray 代替列表并不会太难。然而,ndarrays 似乎不支持使用切片在数组中插入其他项目,就像 Python 列表一样:

>>> a = [0, 0]
>>> a[:1] = [2, 3]
>>> a
[2, 3, 0]
Run Code Online (Sandbox Code Playgroud)

我认为 numpy 的等价物应该是这样的:

>>> a = np.array([0, 0])  # or a = np.zeros([2]), but that's not important here
>>> a = np.hstack(([2, 3], a[1:]))
>>> a
array([2, 3, 0])
Run Code Online (Sandbox Code Playgroud)

稍微复杂一点的情况:

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

相对

>>> a = np.array([1, 2, 3, 4])
>>> a = np.hstack((a[:1], [0, 0, 0], a[3:]))
>>> a
array([1, 0, 0, 0, 4])
Run Code Online (Sandbox Code Playgroud)

当然,上面的 numpy 示例不会像常规 Python 列表扩展那样将结果存储在原始数组中。

  • 您能否举一个例子来说明为什么使用内置 Python 切片执行此操作会产生不良的边缘情况? (5认同)