4

如果我有以下列表:

a = [1, 2, 3]

我运行以下代码:

for x in a:
  x += 1

看来这并没有改变列表a

但是,如果我执行以下操作:

for i in range(0, len(a)):
  a[i] += 1

这将修改“a”的内容。

所以我猜想x并以不同的方式a[i]指代元素。a究竟是什么导致了这种差异?它们各自是如何引用的元素的a

4

4 回答 4

5

当你遍历一个列表时,每个元素都会依次产生。但是,有不同种类的对象。 可变不可变。当您执行以下操作时:

a += 1

对于不可变对象,它大致转换为:

a = a + 1

现在在这种情况下,您通过 获取对象引用a,将 1 添加到它以创建一个新对象。然后为该新对象分配名称a。请注意,如果我们在迭代时这样做,我们根本不会触及列表——我们只是不断地创建新对象并将它们分配给 name a

这对于可变对象是不同的。然后a += 1居然就地改变了对象。因此,列表将看到更改,因为它所持有的对象已更改(突变)。(对于不可变对象,列表中包含的对象没有改变,因为它不可能)。有关更多信息,请参阅此问题

这也使您在按索引迭代时更清楚地知道发生了什么。您构造了一个新整数并将其放入列表中(忘记之前该插槽中的任何内容)。

于 2013-04-17T00:55:21.767 回答
3

当你说,

for x in [1,2,3]:
    x+=1

您是说,暂时将 x 保存为变量,然后在该临时保存中添加一个。当您进行下一次迭代时,垃圾人会破坏该变量,因为它是临时的。x 不是列表中的位置。它是列表中该位置的值。

编辑:当我说它被删除时,我并不清楚我的话。发生的情况是,每次通过循环时,x 都会被另一个值替换,所以之前发生的事情就消失了(除非你用它做了其他事情)。但是,使用您正在使用的操作,您不会更改列表中任何元素的值。我对混乱不好。

当你以另一种方式做时,

for x in range(len(lst)):
    lst[x] += 1

然后你在谈论列表的值。x 是变量的索引,因此可以在该位置更改列表值的值。

于 2013-04-17T00:59:16.993 回答
2

这里重要的概念是引用的概念。在 Python 中,变量是对位于内存某处的对象的引用。让我们使用箭头 → 来表示引用。变量→对象。左边是变量,右边是对象。

可以将数组可视化为引用三个整数对象的三个变量。

a[0] → int(1)
a[1] → int(2)
a[2] → int(3)

现在,整数对象是不可变的。它们不能改变。当您更改整数变量时,您不会更改变量所引用的对象。你不能,因为ints 是不可变的。您可以做的是使变量引用不同的对象。

直接更新

让我们先看看第二个循环,因为它更简单。如果直接更新数组会发生什么?

for i in range(0, len(a)):
  a[i] += 1

首先让我们展开循环:

a[0] += 1
a[1] += 1
a[2] += 1

对于整数,a[0] += 1等价于a[0] = a[0] + 1。首先,Python 评估a[0] + 1并获得结果int(2)。然后它更改a[0]为 reference int(2)。第二个和第三个语句的评估类似。

a = [1, 2, 3]  # a[0] → int(1)
               # a[1] → int(2)
               # a[2] → int(3)

a[0] += 1      # a[0] → int(2)
a[1] += 1      # a[1] → int(3)
a[2] += 1      # a[2] → int(4)

间接更新

那我称之为“间接”更新呢?

for x in a:
  x += 1

展开循环会产生这一系列等效的语句:

x = a[0]
x += 1
x = a[1]
x += 1
x = a[2]
x += 1

每一步会发生什么,为什么数组没有改变?

x = a[0]

这使得x引用任何对象a[0]引用。两者a[0]和都x引用同一个int(1)对象,但x不直接连接到a[0]. 它指的是所指的东西a[0],而不是a[0]它本身。

x += 1

这改变了x所指的内容。它对 没有影响a[0]

第二个和第三个作业也会发生同样的事情。结果是x不断变化,而元素a只是被读取但从未被修改。所以当循环终止时a是不变的。

a = [1, 2, 3]  # a[0] → int(1)
               # a[1] → int(2)
               # a[2] → int(3)

x = a[0]       # x → int(1)
x += 1         # x → int(2)
x = a[1]       # x → int(2)
x += 1         # x → int(3)
x = a[2]       # x → int(3)
x += 1         # x → int(4)
于 2013-04-17T01:09:40.077 回答
1

把它想象成在你的第一个循环中,x 只是 a 中每个元素的替代品。它不是实际的元素a(尽管id()这是因为它们都引用了同一个对象)。当你这样做x += 1时,你只是在改变x列表中的值而不是值。

在您的第二个 for 循环中,您实际上是通过执行a[i] += 1.

于 2013-04-17T00:52:18.133 回答