-2

运行这个:

a = [[1], [2]]
for i in a:
    i *= 2
print(a)

[[1, 1], [2, 2]]

我希望得到原始列表,就像这里发生的那样:

a = [1, 2]
for i in a:
    i *= 2
print(a)

这使:

[1, 2]

为什么要修改第一个示例中的列表?

4

3 回答 3

3

您正在使用扩充的赋值语句。这些对左侧命名的对象进行操作,使该对象有机会就地更新:

像 x += 1 这样的扩充赋值表达式可以重写为 x = x + 1 以实现类似但不完全相等的效果。在增强版本中,x 只被评估一次。此外,如果可能,实际操作会在原地执行,这意味着不是创建新对象并将其分配给目标,而是修改旧对象。

(粗体强调我的)。

这是通过让对象实现__i[op]__方法来实现的,因为=*这就是__imul__钩子

调用这些方法来实现增强的算术赋值(+=, -=, *=, @=, /=, //=, %=, **=, <<=, >>=, &=, ^=, |=)。这些方法应该尝试就地执行操作(修改self)并返回结果(可以是,但不一定是self)。

在列表上使用会将该列表对象*=相乘并返回相同的列表对象 ( ) 以“分配”回相同的名称。self

另一方面,整数是不可变的对象。整数的算术运算返回新的整数对象,所以int对象甚至不实现__imul__钩子;在这种情况下,Python 必须回退到执行i = i * 3

所以对于第一个例子,代码:

a = [[1], [2]]
for i in a:
    i *= 2

确实是这样做的(出于说明目的,展开了循环):

a = [[1], [2]]
i = a[0].__imul__(2)  # a[0] is altered in-place
i = a[1].__imul__(2)  # a[1] is altered in-place

list.__imul__方法将更改应用于列表对象本身,返回对列表对象的引用。

对于整数,将改为执行:

a = [1, 2]
i = a[0] * 2  # a[0] is not affected
i = a[1] * 2  # a[1] is not affected

所以现在的整数对象被分配给i,它独立于a.

于 2017-07-05T13:40:00.723 回答
1

每个示例的结果不同的原因是因为list 是可变的但整数不是

这意味着当您就地修改整数对象时,运算符必须返回一个新的整数对象。但是,由于列表是可变的,因此更改只是添加到已经存在的列表对象中。

当您*=在第一个示例中使用 for 循环时,Python 会修改已经存在的列表。但是当您使用*=整数时,必须返回一个新的整数对象。

这也可以通过一个简单的例子来观察:

>>> a = 1
>>> b = [1]
>>> 
>>> id(a)
1505450256
>>> id(b)
52238656
>>> 
>>> a *= 1
>>> b *= 1
>>> 
>>> id(a)
1505450256
>>> id(b)
52238656
>>>

正如你在上面看到的,a当我们就地相乘时,内存地址发生了变化。所以*=返回了一个新对象。但是列表的内存没有改变。这意味着*=就地修改了列表对象。

于 2017-07-05T13:40:44.817 回答
0

在第一种情况下,你ifor循环是一个list. 所以你是在告诉 python 嘿,拿这个ith列表并重复两次。您基本上是重复列表 2 次,这就是*操作员对列表所做的操作。
在第二种情况下,你i是一个值,所以你申请*的是一个值,而不是一个列表。

于 2017-07-05T13:23:26.817 回答