8

考虑以下会话。差异如何解释?我认为这a += b是(因此等同于)的语法糖a = a + b。显然我错了。

>>> import numpy as np
>>> a  = np.arange(24.).reshape(4,6)
>>> print a
[[  0.   1.   2.   3.   4.   5.]
 [  6.   7.   8.   9.  10.  11.]
 [ 12.  13.  14.  15.  16.  17.]
 [ 18.  19.  20.  21.  22.  23.]]
>>> for line in a:
...     line += 100
...
>>> print a #a has been changed
[[ 100.  101.  102.  103.  104.  105.]
 [ 106.  107.  108.  109.  110.  111.]
 [ 112.  113.  114.  115.  116.  117.]
 [ 118.  119.  120.  121.  122.  123.]]
>>>
>>> for line in a:
...     line = line + 999
...
>>> print a #a hasn't been changed
[[ 100.  101.  102.  103.  104.  105.]
 [ 106.  107.  108.  109.  110.  111.]
 [ 112.  113.  114.  115.  116.  117.]
 [ 118.  119.  120.  121.  122.  123.]]

谢谢

4

2 回答 2

15

使用+运算符会调用特殊方法,该方法__add__应该创建一个新对象并且不应该修改原始对象。

另一方面,如果可能,使用+=运算符会导致调用__iadd__应该修改对象而不是创建新对象。

__add__

调用这些方法来实现二进制算术运算(+、-、*、//、%、divmod()、pow()、**、<<、>>、&、^、|)。例如,要计算表达式 x + y,其中 x 是具有 __add__() 方法的类的实例,调用 x.__add__(y)。

__iadd__

调用这些方法来实现增强的算术赋值(+=、-=、*=、/=、//=、%=、**=、<<=、>>=、&=、^=、|= )。这些方法应该尝试就地执行操作(修改 self)并返回结果(可能是但不一定是 self)。

当然,如果您愿意,可以实现__add____iadd__具有其他一些行为,但您观察到的是标准和推荐的方式。而且,是的,你第一次看到它时会有点惊讶。

于 2010-08-10T09:18:23.360 回答
7

你没看错,有时a += b确实是 的语法糖a = a + b,但有时不是,这是 Python 更令人困惑的特性之一 - 请参阅这个类似的问题以获得更多讨论。

+运算符调用特殊方法,运算符__add__尝试调用就地特殊方法,但我认为在未定义的情况下值得扩展。+=__iadd____iadd__

如果未定义就地运算符,例如对于字符串和整数等不可变类型,则__add__调用 then 。所以对于这些类型来说,a += b真的是语法糖a = a + b。这个玩具类说明了这一点:

>>> class A(object):
...     def __add__(self, other):
...         print "In __add__ (not __iadd__)"
...         return A()
...
>>> a = A()
>>> a = a + 1
In __add__ (not __iadd__)
>>> a += 1
In __add__ (not __iadd__)

这是您对任何不可变类型都应该期望的行为。虽然这可能会令人困惑,但替代方法是禁止+=不可变类型,这将是不幸的,因为这意味着您不能在字符串或整数上使用它!

再举一个例子,这导致列表和元组之间的差异,两者都支持+=,但只能修改列表:

>>> a = (1, 2)
>>> b = a
>>> b += (3, 4)   # b = b + (3, 4)   (creates new tuple, doesn't modify)
>>> a
(1, 2)

>>> a = [1, 2]
>>> b = a
>>> b += [3, 4]   # calls __iadd___ so modifies b (and so a also)
>>> a
[1, 2, 3, 4]

当然,所有其他就地操作符也是如此,-=, *=, //=,%=等。

于 2010-08-10T10:50:11.720 回答