Python 增强赋值问题

15 投票
4 回答
5679 浏览
提问于 2025-04-16 08:12

我遇到了一些有趣的事情,关于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 += ba = a + b是一样的,那它们在底层实现上有什么区别呢?

4 个回答

1

a = a + ba += 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

如果arraynumpy.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不知道怎么加,而ab是不同类型的时候才会用到。
  • __iadd__: 这个是就地加法;用于a += b这种情况,结果会直接赋值回左边的变量。这个方法单独提供是因为可能有更高效的实现方式。例如,如果a是一个列表,那么a += b其实和a.extend(b)是一样的。但是在c = a + b的情况下,你必须先复制一份a再去扩展,因为这里不想修改原来的a。要注意的是,如果你没有实现__iadd__,Python就会直接调用__add__

所以因为这些不同的操作是通过不同的方法来实现的,有可能(虽然一般不推荐)让它们做完全不同的事情,或者在某些情况下,只是稍微不同的事情。

其他人推测你在使用NumPy,并解释了它的行为。不过,你问的是底层的实现。希望你现在明白了为什么有时候a += ba = a + b是不一样的。顺便提一下,类似的三组方法也可以用于其他操作。你可以查看这个页面,了解所有支持的就地方法。

撰写回答