运行此:
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]
为什么修改第一个示例中的列表?
答案 0 :(得分:3)
您正在使用augmented assignment statements。它们对左侧命名的对象进行操作,使该对象有机会更新就地:
像x + = 1这样的增强赋值表达式可以重写为x = x + 1,以实现类似但不完全相同的效果。在增强版本中,x仅评估一次。 此外,如果可能,实际操作是就地执行的,这意味着不是创建新对象并将其分配给目标,而是修改旧对象。
(大胆强调我的)。
这是通过让对象实现__i[op]__
方法实现的,=*
是__imul__
hook:
调用这些方法来实现增强的算术分配(
+=
,-=
,*=
,@=
,/=
,//=
,%=
,**=
,<<=
,>>=
,&=
,^=
,|=
)。 这些方法应尝试就地执行操作(修改self
)并返回结果(可能是,但不一定是self
)。< / p>
在列表上使用*=
会将列表对象与返回相同的列表对象(self
),以便“分配”回相同的名称。
另一方面,整数是不可变对象。对整数的算术运算返回 new 整数对象,因此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
所以现在 new 整数对象被分配给i
,它独立于a
。
答案 1 :(得分:1)
每个示例的结果不同的原因是列表可变,但整数不是。
这意味着当您修改整数对象时,运算符必须返回一个新的整数对象。但是,由于列表是可变的,因此只需将更改添加到现有列表对象中。
在第一个示例中的for循环中使用*=
时,Python会修改现有列表。但是当你使用*=
和整数时,必须返回一个新的整数对象。
这也可以通过一个简单的例子来观察:
>>> a = 1
>>> b = [1]
>>>
>>> id(a)
1505450256
>>> id(b)
52238656
>>>
>>> a *= 1
>>> b *= 1
>>>
>>> id(a)
1505450256
>>> id(b)
52238656
>>>
如上所示,a
的内存地址在我们就地增加时会发生变化。所以*=
返回了一个新对象。但是列表的内存没有改变。这意味着*=
就地修改了列表对象。
答案 2 :(得分:0)
在第一种情况下,i
循环中的for
为list
。所以你告诉python嘿,拿ith
列表并重复两次。您基本上是重复列表2次,这是*
运算符对列表所做的操作。
在第二种情况下,您的i
是一个值,因此您正在应用{{ 1}}到一个值,而不是一个列表。