【问题标题】:Overriding "+=" in Python? (__iadd__() method)在 Python 中覆盖“+=”? (__iadd__() 方法)
【发布时间】:2010-11-06 00:43:35
【问题描述】:

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

【问题讨论】:

    标签: python operators overriding


    【解决方案1】:

    是的,覆盖__iadd__ 方法。示例:

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

    【讨论】:

    • 如果你的类代表不可变对象,你不应该实现__iadd__。在这种情况下,只需实现__add__,它将用于覆盖+=。例如,您可以在字符串和整数等不可变类型上使用 +=,而使用 __iadd__ 则无法做到这一点。
    • @ScottGriffiths,你是说如果你已经实现了__add__,你就不一定要实现__iadd__?我阅读了您链接的重复问题,但我只是感到困惑,因为我不明白您为什么要实现 __add__ 以便它改变对象
    • @ScottGriffiths 的意思是问“你不一定要实现 __iadd__ 才能使用 +=?”
    • @JosieThompson:如果您不实现__iadd__,那么它将使用__add__(如果可用),这通常很好。所以在这种情况下,a += b 将等同于a = a + b,它为a 分配一个新值,而不是更改a 本身。拥有一个单独的 __iadd__ 通常是一个很好的优化,而不是您需要使用 += 运算符。
    • @ScottGriffiths __imul__ 也一样吗?
    【解决方案2】:

    除了上述答案中正确给出的内容外,值得明确说明的是,当 __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__是这样设计的吗?即为什么它分配返回值而不是仅仅解决就地突变?
    • @joel 因为这就是它必须与不可变类型(例如strint,更重要的是tuplenamedtuple)一起工作的方式。
    【解决方案3】:

    除了重载__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.idy.idResource.class_counter 具有什么值?

    【讨论】:

    • 您的第二个示例与 iadd 或 += 无关。如果你使用 self.class_counter = self.class_counter + 1 也会出现同样的结果。这只是一个范围问题,应该使用 Resource 时使用 self。
    • 这是一个使用 += 会导致问题的例子。如果您正在重载 iadd,那么您就是在向您的班级的用户(包括您自己)开放,至少,您应该事先知道问题的存在。
    • @FogleBird:这是一个陷阱,因为foo += bar 可以表示“改变foo 引用的现有对象”或“将foo 分配给由表达式foo + bar 产生的对象”。这取决于foo 是否有__iadd__ 方法。
    【解决方案4】:

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

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

    【讨论】:

      猜你喜欢
      • 2016-12-23
      • 1970-01-01
      • 2015-03-29
      • 1970-01-01
      • 2016-02-28
      • 1970-01-01
      • 2010-10-27
      • 2013-01-31
      • 1970-01-01
      相关资源
      最近更新 更多