为什么 += 在列表上表现出乎意料? [英] Why does += behave unexpectedly on lists?

查看:33
本文介绍了为什么 += 在列表上表现出乎意料?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

python 中的 += 运算符似乎在列表上意外运行.谁能告诉我这里发生了什么?

class foo:酒吧 = []def __init__(self,x):self.bar += [x]类 foo2:酒吧 = []def __init__(self,x):self.bar = self.bar + [x]f = foo(1)g = foo(2)打印 f.bar打印 g.barf.bar += [3]打印 f.bar打印 g.barf.bar = f.bar + [4]打印 f.bar打印 g.barf = foo2(1)g = foo2(2)打印 f.bar打印 g.bar

输出

[1, 2][1, 2][1, 2, 3][1, 2, 3][1, 2, 3, 4][1, 2, 3][1][2]

foo += bar 似乎会影响类的每个实例,而 foo = foo + bar 似乎表现得像我期望的那样.

+= 运算符称为复合赋值运算符".

解决方案

一般的答案是 += 尝试调用 __iadd__ 特殊方法,如果不是不可用,它尝试使用 __add__ 代替.所以问题在于这些特殊方法之间的区别.

__iadd__ 特殊方法用于就地添加,即它改变它所作用的对象.__add__ 特殊方法返回一个新对象,也用于标准 + 运算符.

因此,当 += 运算符用于定义了 __iadd__ 的对象时,该对象将被修改.否则它将尝试使用普通的 __add__ 并返回一个新对象.

这就是为什么对于像列表这样的可变类型 += 会改变对象的值,而对于像元组、字符串和整数这样的不可变类型会返回一个新的对象而不是(a += b 等同于 a = a + b).

对于同时支持 __iadd____add__ 的类型,因此您必须小心使用哪一种.a += b 将调用 __iadd__ 并改变 a,而 a = a + b 将创建一个新对象并将其分配给 a.它们不是同一个操作!

<预><代码>>>>a1 = a2 = [1, 2]>>>b1 = b2 = [1, 2]>>>a1 += [3] # 使用_​​_iadd__,就地修改a1>>>b1 = b1 + [3] # 使用 __add__,创建新列表,并将其分配给 b1>>>a2[1, 2, 3] # a1 和 a2 仍然是同一个列表>>>b2[1, 2] # 而只有 b1 被改变

对于不可变类型(您没有 __iadd__)a += ba = a + b 是等效的.这就是允许您在不可变类型上使用 += 的原因,这可能看起来是一个奇怪的设计决定,除非您考虑否则您无法在不可变类型(如数字)上使用 +=

The += operator in python seems to be operating unexpectedly on lists. Can anyone tell me what is going on here?

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 

OUTPUT

[1, 2]
[1, 2]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3, 4]
[1, 2, 3]
[1]
[2]

foo += bar seems to affect every instance of the class, whereas foo = foo + bar seems to behave in the way I would expect things to behave.

The += operator is called a "compound assignment operator".

解决方案

The general answer is that += tries to call the __iadd__ special method, and if that isn't available it tries to use __add__ instead. So the issue is with the difference between these special methods.

The __iadd__ special method is for an in-place addition, that is it mutates the object that it acts on. The __add__ special method returns a new object and is also used for the standard + operator.

So when the += operator is used on an object which has an __iadd__ defined the object is modified in place. Otherwise it will instead try to use the plain __add__ and return a new object.

That is why for mutable types like lists += changes the object's value, whereas for immutable types like tuples, strings and integers a new object is returned instead (a += b becomes equivalent to a = a + b).

For types that support both __iadd__ and __add__ you therefore have to be careful which one you use. a += b will call __iadd__ and mutate a, whereas a = a + b will create a new object and assign it to a. They are not the same operation!

>>> 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

For immutable types (where you don't have an __iadd__) a += b and a = a + b are equivalent. This is what lets you use += on immutable types, which might seem a strange design decision until you consider that otherwise you couldn't use += on immutable types like numbers!

这篇关于为什么 += 在列表上表现出乎意料?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆