在Python中覆盖“ + =”?(__iadd __()方法)


Answers:


124

是的,重写该__iadd__方法。例:

def __iadd__(self, other):
    self.number += other.number
    return self    

44
__iadd__如果您的类表示不可变的对象,则不应实现。在这种情况下,只需实现__add__将用于替代的实现即可+=。例如,您可以使用+=不可变的类型(例如字符串和整数),而不能使用__iadd__
Scott Griffiths

@ScottGriffiths,那么您是说,如果您已实施__add__,则不一定要实施__iadd__?我读了您链接的重复问题,但是我很困惑,因为我不明白您为什么要实现__add__它,以便使对象发生变化
Josie Thompson

1
@ScottGriffiths的意思是问“您不一定必须实现__iadd__ 使用+ =吗?”
Josie Thompson

12
@JosieThompson:如果您没有实现__iadd__,它将__add__在可用的情况下使用,通常就可以了。因此,在这种情况下,它a += b等同于a = a + b,它会为其分配一个新值,a而不是更改a自身。__iadd__通常,单独使用是一种不错的优化方法,而不是您需要使用+=运算符的方法。
Scott Griffiths

1
@ScottGriffiths同样适用__imul__吗?
Chris_Rands

26

除了以上答案中正确给出的内容外,值得明确说明的__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上面的例子中,你的目标将是x[2],不x[1]和任何早于x[0]现在将在x[1]和被赋予的返回值__iadd__调用。

除非人们了解发生了什么,否则产生的错误可能是修复的噩梦。


4
您碰巧知道为什么__iadd__会这样设计吗?即,为什么它分配返回值而不是仅仅就地进行突变?
joel

14

除了超载__iadd__(记住要返回自身!)之外,您还可以回退__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.id以及Resource.class_counter有哪些?


9
您的第二个示例与iadd或+ =无关。如果您使用self.class_counter = self.class_counter + 1,则会发生相同的结果。这只是范围问题,在应使用Resource时使用self。
2009年

这是使用+ =可能导致问题的示例。如果您要重载iadd,那么您将为此打开类(包括您自己)的用户,并且至少,您应该事先知道该问题的存在。

4
@FogleBird:这是一个陷阱,因为foo += bar它可能意味着“使foo引用的现有对象发生突变”或“分配foo给由表达式生成的对象foo + bar”。而究竟发生什么取决于是否foo__iadd__方法。
克劳迪(Claudou),2016年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.