84

是否可以在 Python 中覆盖 += ?

4

4 回答 4

141

是的,覆盖该__iadd__方法。例子:

def __iadd__(self, other):
    self.number += other.number
    return self    
于 2009-06-26T02:08:45.960 回答
39

除了上面答案中正确给出的内容之外,值得明确说明的是,何时__iadd__被覆盖,x += y操作不会以__iadd__方法结束而结束。

相反,它以x = x.__iadd__(y). 换句话说,__iadd__在实现完成之后,Python 将实现的返回值分配给您“添加到”的对象。

这意味着可以改变操作的左侧,以x += y使最终的隐式步骤失败。考虑当您添加到列表中的内容时会发生什么:

>>> x[1] += y # x has two items

现在,如果您的__iadd__实现(位于 的对象的方法x[1])错误地或故意删除了x[0]列表开头的第一项( ),那么 Python 将运行您的__iadd__方法)并尝试将其返回值分配给x[1]. 它将不再存在(它将位于x[0]),从而导致ÌndexError.

或者,如果您在上述示例__iadd__的开头插入一些内容x,您的对象将位于 at x[2], not x[1],并且之前的 atx[0]将位于 atx[1]并被分配__iadd__调用的返回值。

除非人们了解正在发生的事情,否则由此产生的错误可能是一场难以修复的噩梦。

于 2015-07-21T18:18:05.160 回答
14

除了重载__iadd__(记得返回 self!),你还可以回退__add__,因为 x += y 将像 x = x + y 一样工作。(这是 += 运算符的缺陷之一。)

>>> class A(object):
...   def __init__(self, x):
...     self.x = x
...   def __add__(self, other):
...     return A(self.x + other.x)
>>> a = A(42)
>>> b = A(3)
>>> print a.x, b.x
42 3
>>> old_id = id(a)
>>> a += b
>>> print a.x
45
>>> print old_id == id(a)
False

它甚至绊倒了专家

class Resource(object):
  class_counter = 0
  def __init__(self):
    self.id = self.class_counter
    self.class_counter += 1

x = Resource()
y = Resource()

您期望x.idy.idResource.class_counter具有什么值?

于 2009-06-26T02:26:01.510 回答
5

http://docs.python.org/reference/datamodel.html#emulating-numeric-types

例如,要执行语句 x += y,其中 x 是具有 __iadd__() 方法的类的实例,调用 x.__iadd__(y)。

于 2009-06-26T02:09:16.600 回答