什么时候是;i+;=x";不同于;i=i+;x";用Python?

什么时候是;i+;=x";不同于;i=i+;x";用Python?,python,operators,Python,Operators,有人告诉我,+=的效果可能不同于I=I+的标准符号。是否存在i+=1与i=i+1不同的情况?这完全取决于对象i +=调用(如果它存在——如果它不存在,则返回到\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu),而+调用1或2 从API的角度来看,\uuuuu iadd\uuuuuu应该用于修改原地可变对象(返回已变异的对象),而\uuuuuu add\uuuuuu应该返回某个对象的新实例。对于不可变对象,这两个方法

有人告诉我,
+=
的效果可能不同于
I=I+
的标准符号。是否存在
i+=1
i=i+1
不同的情况?

这完全取决于对象
i

+=
调用(如果它存在——如果它不存在,则返回到
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
),而
+
调用1或2

从API的角度来看,
\uuuuu iadd\uuuuuu
应该用于修改原地可变对象(返回已变异的对象),而
\uuuuuu add\uuuuuu
应该返回某个对象的新实例。对于不可变对象,这两个方法都会返回一个新实例,但
\uuu iadd\uuu
会将新实例放在当前名称空间中,其名称与旧实例相同。这就是为什么

i = 1
i += 1
似乎增加了
i
。实际上,您会得到一个新的整数,并将其分配到“
i
”的“顶部”——丢失对旧整数的一个引用。在这种情况下,
i+=1
i=i+1
完全相同。但是,对于大多数可变对象,情况就不同了:

作为一个具体例子:

a = [1, 2, 3]
b = a
b += [1, 2, 3]
print a  #[1, 2, 3, 1, 2, 3]
print b  #[1, 2, 3, 1, 2, 3]
与之相比:

a = [1, 2, 3]
b = a
b = b + [1, 2, 3]
print a #[1, 2, 3]
print b #[1, 2, 3, 1, 2, 3]
注意,在第一个示例中,由于
b
a
引用同一个对象,当我在
b
上使用
+=
时,它实际上改变了
b
(而且
a
也看到了这种改变——毕竟,它引用的是同一个列表)。但是,在第二种情况下,当我执行
b=b+[1,2,3]
时,这将获取
b
正在引用的列表,并将其与新列表
[1,2,3]
连接起来。然后,它在当前名称空间中将连接的列表存储为
b
——而不考虑前一行是什么


1在表达式
x+y
中,如果
x.\uuuu-add\uuuu
未实现或
x.\uu-add\uuuuuu(y)
返回
未实现
x
y
具有不同的类型,则
x+y
尝试调用。那么,如果你有

foo\u实例+=bar\u实例

如果
Foo
未实现
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu添加
\uuuuuuuuuuuuuuuuuuuuuuuuu

foo\u instance=bar\u instance.\uu radd\uuu(bar\u instance,foo\u instance)


2在表达式
foo\u instance+bar\u instance
中,如果
bar\u instance
的类型是
foo\u instance
类型的子类(例如
issubclass(bar,foo)
),则在
foo\u instance+bar\u instance
之前将尝试
。其基本原理是,
Bar
在某种意义上比
Foo
是一个“更高级”的对象,因此
Bar
应该可以选择覆盖
Foo
的行为。

在封面下,
i+=1
做了如下操作:

try:
    i = i.__iadd__(1)
except AttributeError:
    i = i.__add__(1)
i = i.__add__(1)
i=i+1
则执行以下操作:

try:
    i = i.__iadd__(1)
except AttributeError:
    i = i.__add__(1)
i = i.__add__(1)
这有点过于简单了,但您明白了:Python为类型提供了一种处理
+=
的方法,特别是通过创建
\uu iadd\uuuuu
方法以及
\uuuu add\uu

其目的是,可变类型(如
list
)将在
\uuu iadd\uuuu
中发生变异(然后返回
self
,除非您正在做一些非常棘手的事情),而不可变类型(如
int
)将无法实现它

例如:

>>> l1 = []
>>> l2 = l1
>>> l1 += [3]
>>> l2
[3]
因为
l2
是与
l1
相同的对象,并且您变异了
l1
,所以您也变异了
l2

但是:

在这里,你没有变异
l1
;相反,您创建了一个新列表,
l1+[3]
,并将名称
l1
反弹指向它,使
l2
指向原始列表


(在
+=
版本中,您还重新绑定了
l1
,只是在这种情况下,您将其重新绑定到已绑定到的相同
列表中,因此您通常可以忽略该部分。)

下面是一个直接比较
i+=x
i=i+x
的示例:

def foo(x):
  x = x + [42]

def bar(x):
  x += [42]

c = [27]
foo(c); # c is not changed
bar(c); # c is changed to [27, 42]

好的,
+=
调用
\uuu iadd\uuu
,如果它存在,则返回到添加和重新绑定。这就是为什么
i=1;i+=1
即使没有
int.\uuu iadd\uuu
也可以工作。但是除了那个小错误,还有很好的解释。@abarnert--我一直认为
int.\uuuu iadd\uuuuu
刚刚被称为
\uu add\uuuu
。我很高兴今天学到了一些新东西:)@abarnert——我想可能是完整的,
x+y
调用
y。
x
如果
x.\uu添加
不存在(或者返回
NotImplemented
x
y
是不同的类型),如果你真的想成为一个完整者,您必须提到,“if it exists”位通过通常的getattr机制,除了经典类的一些怪癖,对于C API中实现的类型,它会查找
nb\u inplace\u add
sq\u inplace\u concat
,这些C API函数比Python dunder方法有更严格的要求,而且……但我认为这与答案无关。主要区别在于,
+=
尝试在返回到像
+
那样的行为之前进行就地添加,我想你已经解释过了。是的,我想你是对的。。。尽管我可以回到C API不是python的一部分的立场上。这是Cpython的一部分:-PDOS
\uuuu iadd\uuuuu
在发生
属性错误时实际调用
\uuuu add\uuuuuuu
?嗯,
i.\uuuu iadd\uuuuuu
不调用
\uuuuuu add\uuuu
;是
i+=1
调用
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
.errr。。。是的,我就是这个意思。有趣。我没有意识到这是自动完成的。首先