在 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)
这里a
、b
和i
是表达式,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函数
Python中的列表元组切片增删改查#count:计算某元素出现次数找位置#index#reverse()反转#sort()