为什么原来的名单变了?
Why does the original list change?
运行这个:
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]
为什么修改了第一个例子中的列表?
在第一种情况下,for
循环中的 i
是 list
。所以你告诉 python 嘿,拿 ith
列表并重复两次。您基本上是在重复列表 2 次,这就是 *
运算符对列表所做的。
在第二种情况下,您的 i
是一个值,因此您正在应用 *
到一个值,而不是列表。
您正在使用 augmented assignment statements。这些对左侧命名的对象进行操作,使该对象有机会更新 就地:
An augmented assignment expression like x += 1 can be rewritten as x = x + 1 to achieve a similar, but not exactly equal effect. In the augmented version, x is only evaluated once. Also, when possible, the actual operation is performed in-place, meaning that rather than creating a new object and assigning that to the target, the old object is modified instead.
(大胆强调我的)。
这是通过让对象实现 __i[op]__
方法来实现的,因为 =*
即 __imul__
hook:
These methods are called to implement the augmented arithmetic assignments (+=
, -=
, *=
, @=
, /=
, //=
, %=
, **=
, <<=
, >>=
, &=
, ^=
, |=
). These methods should attempt to do the operation in-place (modifying self
) and return the result (which could be, but does not have to be, self
).
在列表上使用 *=
将 该列表对象 和 return 相乘为相同的列表对象 (self
) 'assigned'回同名.
另一方面,整数是不可变的对象。对整数的算术运算 return 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__
方法将更改应用于列表对象本身, 和 return 是对列表对象的引用。
对于整数,改为执行:
a = [1, 2]
i = a[0] * 2 # a[0] is not affected
i = a[1] * 2 # a[1] is not affected
所以现在 new 整数对象被分配给 i
,它独立于 a
.
每个示例的结果不同的原因是因为列表是可变的,但整数不是。
这意味着当您就地修改整数对象时,运算符必须 return 一个新的整数对象。但是,由于列表是可变的,因此只需将更改添加到已经存在的列表对象中即可。
当您在第一个示例的 for 循环中使用 *=
时,Python 修改已经存在的列表。但是,当您将 *=
与整数一起使用时,必须 return 编辑一个新的整数对象。
这也可以通过一个简单的例子来观察:
>>> a = 1
>>> b = [1]
>>>
>>> id(a)
1505450256
>>> id(b)
52238656
>>>
>>> a *= 1
>>> b *= 1
>>>
>>> id(a)
1505450256
>>> id(b)
52238656
>>>
正如您在上面看到的,a
的内存地址在我们就地相乘时发生了变化。所以 *=
return 编辑了一个新对象。但是列表的内存没有改变。这意味着 *=
就地修改了列表对象。
运行这个:
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]
为什么修改了第一个例子中的列表?
在第一种情况下,for
循环中的 i
是 list
。所以你告诉 python 嘿,拿 ith
列表并重复两次。您基本上是在重复列表 2 次,这就是 *
运算符对列表所做的。
在第二种情况下,您的 i
是一个值,因此您正在应用 *
到一个值,而不是列表。
您正在使用 augmented assignment statements。这些对左侧命名的对象进行操作,使该对象有机会更新 就地:
An augmented assignment expression like x += 1 can be rewritten as x = x + 1 to achieve a similar, but not exactly equal effect. In the augmented version, x is only evaluated once. Also, when possible, the actual operation is performed in-place, meaning that rather than creating a new object and assigning that to the target, the old object is modified instead.
(大胆强调我的)。
这是通过让对象实现 __i[op]__
方法来实现的,因为 =*
即 __imul__
hook:
These methods are called to implement the augmented arithmetic assignments (
+=
,-=
,*=
,@=
,/=
,//=
,%=
,**=
,<<=
,>>=
,&=
,^=
,|=
). These methods should attempt to do the operation in-place (modifyingself
) and return the result (which could be, but does not have to be,self
).
在列表上使用 *=
将 该列表对象 和 return 相乘为相同的列表对象 (self
) 'assigned'回同名.
另一方面,整数是不可变的对象。对整数的算术运算 return 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__
方法将更改应用于列表对象本身, 和 return 是对列表对象的引用。
对于整数,改为执行:
a = [1, 2]
i = a[0] * 2 # a[0] is not affected
i = a[1] * 2 # a[1] is not affected
所以现在 new 整数对象被分配给 i
,它独立于 a
.
每个示例的结果不同的原因是因为列表是可变的,但整数不是。
这意味着当您就地修改整数对象时,运算符必须 return 一个新的整数对象。但是,由于列表是可变的,因此只需将更改添加到已经存在的列表对象中即可。
当您在第一个示例的 for 循环中使用 *=
时,Python 修改已经存在的列表。但是,当您将 *=
与整数一起使用时,必须 return 编辑一个新的整数对象。
这也可以通过一个简单的例子来观察:
>>> a = 1
>>> b = [1]
>>>
>>> id(a)
1505450256
>>> id(b)
52238656
>>>
>>> a *= 1
>>> b *= 1
>>>
>>> id(a)
1505450256
>>> id(b)
52238656
>>>
正如您在上面看到的,a
的内存地址在我们就地相乘时发生了变化。所以 *=
return 编辑了一个新对象。但是列表的内存没有改变。这意味着 *=
就地修改了列表对象。