2010-08-10 38 views
8

考虑以下会话。差异如何解释?我认为a += b是(并因此等于)a = a + b的语法糖。显然我错了。通过引用或python中的值处理数据

>>> 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.]] 

谢谢

回答

15

到特殊的方法__add__应该创建一个新的对象,不应该修改原来的呼叫使用+操作结果。

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

__add__

这些方法称为实施二进制算术运算(+, - ,*,//,%,divmod(),POW(),**,< <,> >,&,^,|)。例如,要评估表达式x + y,其中x是具有__add __()方法的类的实例,则调用x .__ add __(y)。

__iadd__

这些方法称为实现增强算术任务(+ =, - =,* =,/ =,// =,(%)=,** =,< < =,> > =,& =,^ =,| =)。 这些方法应尝试就地操作(修改自我)并返回结果(可能是,但不一定是自己)。

当然是可以实现__add____iadd__有一些其他的行为,如果你想,但你看到什么是标准和推荐的方式。而且,是的,你第一次看到它有点令人惊讶。

+0

是这种差异特定于蟒或者是它的'+'与'+ ='在编程语言运营商的共同特征? – 2010-08-10 09:52:58

+0

@bgbg:这是针对Python的。在C#中,例如'a = a + b'几乎等同于'a + = b'。 – 2010-08-10 10:05:39

+0

如果您认为+ =作为递增操作而不是快捷方式来添加值,则可以提供帮助。 – 2010-08-10 10:22:49

7

你没看错,有时真的a += ba = a + b语法糖,但随后有时并非如此,这是Python的更混乱的特点之一 - 看到this similar question更多的讨论。

+操作者调用的特殊方法__add__+=操作试图呼叫就地__iadd__特殊的方法,但我认为这是值得推广的,其中没有定义__iadd__的情况。

如果没有定义就地运算符,例如对于不可变类型(如字符串和整数),则调用__add__。所以对于这些类型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] 

。当然,这同样适用于所有其他就地运营商,-=*=//=%=