什么时候是;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。。。是的,我就是这个意思。有趣。我没有意识到这是自动完成的。首先