【发布时间】:2010-11-06 00:43:35
【问题描述】:
是否可以在 Python 中覆盖 +=?
【问题讨论】:
标签: python operators overriding
是否可以在 Python 中覆盖 +=?
【问题讨论】:
标签: python operators overriding
是的,覆盖__iadd__ 方法。示例:
def __iadd__(self, other):
self.number += other.number
return self
【讨论】:
__iadd__。在这种情况下,只需实现__add__,它将用于覆盖+=。例如,您可以在字符串和整数等不可变类型上使用 +=,而使用 __iadd__ 则无法做到这一点。
__add__,你就不一定要实现__iadd__?我阅读了您链接的重复问题,但我只是感到困惑,因为我不明白您为什么要实现 __add__ 以便它改变对象
__iadd__ 才能使用 +=?”
__iadd__,那么它将使用__add__(如果可用),这通常很好。所以在这种情况下,a += b 将等同于a = a + b,它为a 分配一个新值,而不是更改a 本身。拥有一个单独的 __iadd__ 通常是一个很好的优化,而不是您需要使用 += 运算符。
__imul__ 也一样吗?
除了上述答案中正确给出的内容外,值得明确说明的是,当 __iadd__ 被覆盖时,x += y 操作不会以 __iadd__ 方法的结尾结束。
相反,它以x = x.__iadd__(y) 结尾。换句话说,在实现完成之后,Python 会将您的 __iadd__ 实现的返回值分配给您“添加到”的对象。
这意味着可以改变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] 的任何内容现在将位于@ 987654340@ 并被分配__iadd__ 调用的返回值。
除非人们了解正在发生的事情,否则由此产生的错误可能是一场难以修复的噩梦。
【讨论】:
__iadd__是这样设计的吗?即为什么它分配返回值而不是仅仅解决就地突变?
str 和int,更重要的是tuple 和namedtuple)一起工作的方式。
除了重载__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
它甚至trips up experts:
class Resource(object):
class_counter = 0
def __init__(self):
self.id = self.class_counter
self.class_counter += 1
x = Resource()
y = Resource()
您希望x.id、y.id 和Resource.class_counter 具有什么值?
【讨论】:
foo += bar 可以表示“改变foo 引用的现有对象”或“将foo 分配给由表达式foo + bar 产生的对象”。这取决于foo 是否有__iadd__ 方法。
http://docs.python.org/reference/datamodel.html#emulating-numeric-types
例如,执行语句 x += y,其中 x 是 a 的一个实例 具有 __iadd__() 方法的类, x.__iadd__(y) 被调用。
【讨论】: