在 Python 中为切片的元素赋值

Posted

技术标签:

【中文标题】在 Python 中为切片的元素赋值【英文标题】:Assigning a value to an element of a slice in Python 【发布时间】:2011-05-02 14:45:34 【问题描述】:

这是一个关于 Python 如何处理数据和变量的简单问题。我做了很多实验,大部分都弄明白了 Python,除了这一直让我绊倒:

[编辑:为了清楚起见,我将示例分开并重新排列]

示例 1:

>>> a = [[1], 2]
>>> a[0:1]
[[1]]
>>> a[0:1] = [[5]]
>>> a
[[5], 2] # The assignment worked.

示例 2:

>>> a = [[1], 2]
>>> a[0:1][0]
[1]
>>> a[0:1][0] = [5]
>>> a
[[1], 2] # No change?

示例 3:

>>> a = [[1], 2]
>>> a[0:1][0][0]
1
>>> a[0:1][0][0] = 5
>>> a
[[5], 2] # Why now?

谁能给我解释一下这里发生了什么?

到目前为止,答案似乎声称a[0:1] 返回一个新列表,其中包含对a 的第一个元素的引用。但我不明白这如何解释示例 1。

【问题讨论】:

有趣的问题。让我们看看亚历克斯怎么说。 :) 【参考方案1】:

a[0:1] 返回一个新数组,其中包含对数组 [1] 的引用,因此您最终会通过引用调用修改内部数组。

第一种情况不修改 [1] 数组的原因是您正在为复制的外部数组分配一个新的内部数组值。

底线 - a[0:1] 返回数据的副本,但不复制内部数据。

【讨论】:

@dln385:这篇文章的最后一句话回答了你的问题。内部数据(在您的情况下为数字)不会被复制,因此修改它会更改原始数据。 最后一句话解释了为什么a[0:1][0][0] = 5 有效。但是a[0:1] = [[5]] 不会修改内部数据,因此不应该 起作用。我认为它必须作为特例处理,正如pyfunc 似乎暗示的那样。 是的,当赋值语句(即“=”信号)跟随切片引用时,它与完成其他操作时完全不同。明确一点:>>> a[0:1] = [[5]] 等同于 a.__setitem__(slice(0,1,None), [5]) --- 而 a[0:1][0] = 5 等同于:a.__getitem__(slice(0,1,None)).__setitem__(0, 5)【参考方案2】:

我的理解是切片返回一个新对象。也就是说它的返回值是一个新的列表。

因此您不能使用赋值运算符来更改原始列表的值

>>> a = [[1], 2, 3]
>>> k = a[0:2]
>>> id(a)
4299352904
>>> id(k)
4299353552
>>> 

>>> id(a)
4299352904
>>> id(a[0:2])
4299352832

还有更多的玩法

>>> k = 5
>>> 
>>> id(k)
4298182344
>>> a[0] = [1,2]
>>> a
[[1, 2], 2, 3]
>>> id(a)
4299352904
>>> 

[编辑:关于问题的第二部分]

>>> a[0:1] = [[5]]

以下符号通常也称为切片赋值 内置列表的行为是原子的(删除 + 插入)一次性发生。我的理解是自定义序列不允许这样做。

【讨论】:

这是正确的。切片返回一个新对象。将字典键分配给新变量(例如foo = mydict[bar])也是如此。对分配切片的更改不会修改原始引用,因为它们是副本。【参考方案3】:

有三种不同的索引操作,都被转换为方法调用:

a[i] = b => a.__setitem__(i, b) del a[i] => a.__delitem__(i) a[i] 用作表达式 => a.__getitem__(i)

这里abi 是表达式,i 可以包含使用冒号速记语法创建的slice objects。例如:

>>> class C(object):
...     def __setitem__(self, *a):
...             print a
... 
>>> C()[1] = 0
(1, 0)
>>> C()['foo'] = 0
('foo', 0)
>>> C()['foo':'bar'] = 0
(slice('foo', 'bar', None), 0)
>>> C()['foo':'bar',5] = 0
((slice('foo', 'bar', None), 5), 0)

所以在你的第三个例子中发生的事情是这样的:

a[0:1][0][0] = 5

变成

a.__getitem__(slice(0,1)).__getitem__(0).__setitem__(0, 5)

第一个__getitem__ 返回列表的一部分的副本,但第二个__getitem__ 返回其中的实际列表,然后使用__setitem__ 对其进行修改。

另一方面,你的第二个例子变成了

a.__getitem__(slice(0,1)).__setitem__(0, 5)

所以__setitem__ 在切片副本上被调用,原始列表保持不变。

【讨论】:

以上是关于在 Python 中为切片的元素赋值的主要内容,如果未能解决你的问题,请参考以下文章

Python基础:Python切片,浅拷贝深拷贝和赋值,排序,Reduce函数

在numpy中为多个切片赋值

python 列表切片赋值

给切片赋值的元素会打散

Python笔记-2

Python中的列表元组切片增删改查#count:计算某元素出现次数找位置#index#reverse()反转#sort()