Why does += behave unexpectedly on lists?
python中的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | class foo: bar = [] def __init__(self,x): self.bar += [x] class foo2: bar = [] def __init__(self,x): self.bar = self.bar + [x] f = foo(1) g = foo(2) print f.bar print g.bar f.bar += [3] print f.bar print g.bar f.bar = f.bar + [4] print f.bar print g.bar f = foo2(1) g = foo2(2) print f.bar print g.bar |
产量
1 2 3 4 5 6 7 8 | [1, 2] [1, 2] [1, 2, 3] [1, 2, 3] [1, 2, 3, 4] [1, 2, 3] [1] [2] |
一般的答案是,
因此,当在定义了
这就是为什么列表
对于同时支持
1 2 3 4 5 6 7 8 | >>> a1 = a2 = [1, 2] >>> b1 = b2 = [1, 2] >>> a1 += [3] # Uses __iadd__, modifies a1 in-place >>> b1 = b1 + [3] # Uses __add__, creates new list, assigns it to b1 >>> a2 [1, 2, 3] # a1 and a2 are still the same list >>> b2 [1, 2] # whereas only b1 was changed |
对于不可变类型(没有
有关一般情况,请参阅Scott Griffith的回答。但是,在处理像您这样的列表时,
在执行
此示例代码将解释它:
1 2 3 4 5 6 7 8 9 | >>> l = [] >>> id(l) 13043192 >>> l += [3] >>> id(l) 13043192 >>> l = l + [3] >>> id(l) 13059216 |
请注意,当您将新列表重新分配给
由于
这里的问题是,
在
在
"正确"的实施将是:
1 2 3 | class foo: def __init__(self, x): self.bar = [x] |
当然,类属性是完全合法的。实际上,您可以访问和修改它们,而无需创建此类的实例:
1 2 3 4 | class foo: bar = [] foo.bar = [x] |
其他答案似乎已经涵盖了很多内容,但似乎值得引用和参考的是增强型作业PEP 203:
They [the augmented assignment operators] implement the same operator
as their normal binary form, except that the operation is done
`in-place' when the left-hand side object supports it, and that the
left-hand side is only evaluated once.
…
The idea behind augmented
assignment in Python is that it isn't just an easier way to write the
common practice of storing the result of a binary operation in its
left-hand operand, but also a way for the left-hand operand in
question to know that it should operate `on itself', rather than
creating a modified copy of itself.
虽然已经过了很多时间,说了很多正确的话,但没有答案能将两种影响捆绑在一起。
你有两种效果:
在类
注意,实例dict是被修改的,尽管这通常不是必需的,因为类dict已经包含了相同的赋值。所以这个细节几乎没有被注意到——除非你在之后做了一个
然而,在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | f = foo(1) # adds 1 to the class's bar and assigns f.bar to this as well. g = foo(2) # adds 2 to the class's bar and assigns g.bar to this as well. # Here, foo.bar, f.bar and g.bar refer to the same object. print f.bar # [1, 2] print g.bar # [1, 2] f.bar += [3] # adds 3 to this object print f.bar # As these still refer to the same object, print g.bar # the output is the same. f.bar = f.bar + [4] # Construct a new list with the values of the old ones, 4 appended. print f.bar # Print the new one print g.bar # Print the old one. f = foo2(1) # Here a new list is created on every call. g = foo2(2) print f.bar # So these all obly have one element. print g.bar |
您可以使用
顺便说一句:
这里涉及两件事:
1 2 | 1. class attributes and instance attributes 2. difference between the operators + and += for lists |
在类
1 | self.bar.__iadd__([x]) # modifies the class attribute |
它在适当的位置修改列表,并像list方法
在类
1 | self.bar = self.bar + [x] |
可以解构为:该实例没有属性
1 | self.bar = self.bar.__add__([x]) # bar on the lhs is the class attribute |
然后它创建一个实例属性
对于类
相反,类
1 2 3 | f = foo2(4) print f.bar # accessing the instance attribute. prints [4] print f.__class__.bar # accessing the class attribute. prints [] |
希望这能解决问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | >>> elements=[[1],[2],[3]] >>> subset=[] >>> subset+=elements[0:1] >>> subset [[1]] >>> elements [[1], [2], [3]] >>> subset[0][0]='change' >>> elements [['change'], [2], [3]] >>> a=[1,2,3,4] >>> b=a >>> a+=[5] >>> a,b ([1, 2, 3, 4, 5], [1, 2, 3, 4, 5]) >>> a=[1,2,3,4] >>> b=a >>> a=a+[5] >>> a,b ([1, 2, 3, 4, 5], [1, 2, 3, 4]) |