在python中执行列表增强赋值(+=)的动机是什么?

What was the motivation for doing lists augmented assignment (+=) in place in python?

本问题已经有最佳答案,请猛点这里访问。
1
2
3
4
5
>>> list1 = []
>>> list2 = list1
>>> list2 += [1]
>>> print list1
[1]

将此与

1
2
3
4
5
>>> list1 = []
>>> list2 = list1
>>> list2 = list2 + [1]
>>> print list1
[]

"+="-操作修改原始列表是否有原因?

编辑:只是想让我的问题更清楚一点

在我所知道的大多数语言中,'+='-操作符都不是这样工作的,我想知道为什么它在Python中是这样设计的。

一些例子:

红宝石

1
2
3
4
5
irb(main):001:0> l = []
irb(main):002:0> a = l
irb(main):003:0> a += [2]
irb(main):004:0> l
=> []

斯卡拉等。


以引用方式存储在python中的列表。

这意味着当你做list2 = list1的时候,你不是在复制列表——你只是说"list2指的是list1所做的相同的事情",即你最初在做list1 = []时创建的列表。

python将+=指定为列表的意思是"就地追加",因为大多数情况下,当您在列表上使用+=时,这就是您想要做的——通常不希望每次添加元素时都创建新的列表。

因此,当您附加到list2,它"指的是list1所指的同一个对象",然后从list1中读取时,您会看到附加的项目,正如预期的那样,因为它们都指向同一个列表。

然而,对于+,总是会创建一个新的列表,因为在适当的位置修改任何一个操作数都没有意义(因为a+b并不意味着修改ab)。

因此,当您执行list2 = list2 + [1]操作时,您将创建一个新列表,其中包含list21所指向的原始对象的所有内容,然后说list2现在引用了该新列表。因为它现在引用的是一个不同于list1的列表,所以当您从list1阅读时,您仍然可以看到原始列表,没有额外的1


在python 2.6.4文档的第6.2.1节中。(扩充的赋值语句)

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.

[添加强调]


请参阅有关模拟数字类型的文档,该文档描述了实现此行为的方法。这也适用于列表:

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). If a specific method is not defined, the augmented assignment falls back to the normal methods. For instance, to execute the statement x += y, where x is an instance of a class that has an __iadd__() method, x.__iadd__(y) is called. If x is an instance of a class that does not define a __iadd__() method, x.__add__(y) and y.__radd__(x) are considered, as with the evaluation of x + y.


当您执行list2 += [1]操作时,您正在适当地修改列表。这就是为什么你不改变列表指向的引用,但是你直接改变了列表。当您执行list2 = list2 + [1]操作时,将创建一个新列表。

1
2
3
4
5
6
7
8
9
>>> l = []
>>> id(l)
41523720L
>>> l += [3]
>>> id(l)
41523720L     # same as l = []
>>> l = l+[3]
>>> id(l)
41532232L

这就解释了区别。


好吧,因为这就是它的工作原理。编写list2 = list2 + [1]时,创建新列表并将其绑定到list2名称。当您使用+=时,操作会"就地"进行。因此,当list1list2引用同一对象时(这里就是这种情况),可以使用+=操作符修改它。


在Python中,您所认为的变量名在大多数情况下更类似于指针;"="不复制对象,它将新名称绑定到该对象(在其他上下文中为"按引用复制")。因此,list2 = list1意味着这两个名称指向同一个列表,而不仅仅是同一列表的两个副本。因此,"+="修改由两个名称指向的单个列表。

可以按元素(list2 = [i for in list1]或使用copy模块(list2 = copy.copy(list1)复制list元素)


这就是+=的工作原理。一般来说,

1
a += b

方法

1
a = a + b

但是你的特定场景有一个不同的问题。当你写作时

1
list2 = list1

没有复制;list2现在是对同一列表的引用。对list2的任何修改将在list1中可见,反之亦然。

在您的第二个代码片段中,list2 + [1]构造了一个新的列表,该列表随后被分配给list2。由于此副本独立于list1,因此不可见对list1的更改。

(nitpicker's corner:使用运算符重载,可以构造一个对+=的行为不同的类。不要,只是…不要。