Python 增强赋值问题
我遇到了一些有趣的事情,关于Python中的增强赋值运算符+=
。
看起来,当a
是一个“简单”的数据类型时,a += b
并不总是会自动转换数据类型,而a = a + b
似乎总是能正常工作。
下面是一些会进行转换的情况:
a = 1
b = 1j
a = 1
b = 0.5
而这是一个不进行转换的情况:
from numpy import array
a = array([0, 0 ,0])
b = array([0, 0, 1j])
在执行完a += b
后,a
仍然保持为整数矩阵,而不是变成复数矩阵。
我以前一直认为a += b
和a = a + b
是一样的,那它们在底层实现上有什么区别呢?
4 个回答
1
在 a = a + b
和 a += b
之间的区别是,后者会尽量在原地进行加法运算,也就是说会直接修改对象 a
。你可以通过列表很容易地看出这一点。
a = b = [1, 2]
a += [3]
print b # [1, 2, 3]
a = b = [1, 2]
a = a + [3]
print b # [1, 2]
8
如果array
是numpy.array
(你其实没有具体说明),那么出现的问题是因为这些数组的类型是不能改变的。当你创建数组时,如果没有指定类型,它会自己猜一个类型。如果你之后尝试做一些这个类型不支持的操作,比如把它和一个更复杂的类型(比如复数)相加,numpy会知道怎么计算,但它也知道结果只能存储在那个更复杂的类型里。于是它会报错(在我的电脑上,第一次做这种赋值时会出现这个问题),因为结果不符合原来的类型。当你进行普通的加法时,无论如何都需要创建一个新的数组,而numpy会给这个新数组分配正确的类型。
>>> a=numpy.array([1])
>>> a.dtype
dtype('int32')
>>> b=numpy.array([1+1j])
>>> b.dtype
dtype('complex128')
>>> a+b
array([ 2.+1.j])
>>> (a+b).dtype
dtype('complex128')
>>> a+=b
>>> a
array([2])
>>> a.dtype
dtype('int32')
>>>
16
在Python中,对于+
这个运算符,有三个“特殊”的方法可以让对象来实现:
__add__
: 这个方法用来加两个东西(就是+
运算符)。当你写a + b
的时候,其实是调用了a
的__add__
方法,并把b
作为参数传进去。__radd__
: 这个是反向加法;当你写a + b
时,会调用b
的__radd__
方法,并把a
作为实例传进去。这种情况一般是当a
不知道怎么加,而a
和b
是不同类型的时候才会用到。__iadd__
: 这个是就地加法;用于a += b
这种情况,结果会直接赋值回左边的变量。这个方法单独提供是因为可能有更高效的实现方式。例如,如果a
是一个列表,那么a += b
其实和a.extend(b)
是一样的。但是在c = a + b
的情况下,你必须先复制一份a
再去扩展,因为这里不想修改原来的a
。要注意的是,如果你没有实现__iadd__
,Python就会直接调用__add__
。
所以因为这些不同的操作是通过不同的方法来实现的,有可能(虽然一般不推荐)让它们做完全不同的事情,或者在某些情况下,只是稍微不同的事情。
其他人推测你在使用NumPy,并解释了它的行为。不过,你问的是底层的实现。希望你现在明白了为什么有时候a += b
和a = a + b
是不一样的。顺便提一下,类似的三组方法也可以用于其他操作。你可以查看这个页面,了解所有支持的就地方法。