这是一个关于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的。
答案 0 :(得分:7)
a [0:1]返回一个新数组,其中包含对数组[1]的引用,因此您最终会通过引用调用修改内部数组。
第一种情况不修改[1]数组的原因是你要为复制的外部数组分配一个新的内部数组值。
底线 - a [0:1]返回数据副本,但不会复制内部数据。
答案 1 :(得分:3)
我的理解是切片返回一个新对象。那就是它的返回值是一个新列表。
因此,您无法使用赋值运算符更改原始列表的值
>>> 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]]
以下表示法通常也称为切片分配 内置列表的行为是原子的(删除+插入)一次性发生。我的理解是,自定义序列不允许这样做。
答案 2 :(得分:1)
有三个不同的索引操作,都转换为方法调用:
a[i] = b
=> a.__setitem__(i, b)
del a[i]
=> a.__delitem__(i)
a[i]
用作表达式=> a.__getitem__(i)
此处a
,b
和i
是表达式,i
可以包含使用冒号速记语法创建的slice objects。 E.g:
>>> 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__
,原始列表保持不变。