7

这是一个关于 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。

4

3 回答 3

7

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

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

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

于 2010-10-29T20:13:44.097 回答
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]]

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

于 2010-10-29T20:15:18.477 回答
1

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

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

这里a,bi是表达式,并且i可以包含使用冒号速记语法创建的切片对象。例如:

>>> 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__在切片副本上被调用,原始列表保持不变。

于 2010-10-29T21:56:06.097 回答