ZetCode

Python __iadd__ 方法

最后修改于 2025 年 4 月 8 日

本综合指南探讨 Python 的 __iadd__ 方法,这是一个特殊的魔法方法,用于实现带有 += 运算符的就地加法。我们将介绍基本用法、可变与不可变类型以及实际示例。

基本定义

__iadd__ 方法实现了就地加法运算 (+=)。它应该就地修改对象并返回结果(通常是 self)。

主要特征:它直接修改对象,对于可变对象返回 self,对于不可变类型可以返回一个新对象。它在使用 += 运算符时被调用。

基本 __iadd__ 实现

这是一个简单的实现,展示了 __iadd__ 如何与自定义类一起工作。这演示了就地加法的基本行为。

basic_iadd.py
class Accumulator:
    def __init__(self, value):
        self.value = value
    
    def __iadd__(self, other):
        self.value += other
        return self
    
    def __repr__(self):
        return f"Accumulator({self.value})"

acc = Accumulator(5)
acc += 3
print(acc)  # Accumulator(8)

此示例展示了一个简单的累加器,它可以就地添加值。__iadd__ 方法修改实例的值并返回 self。

+= 运算符调用 __iadd__ (如果可用),如果未实现,则回退到 __add__。此方法应返回 self 以进行正确的链式调用。

可变对象与不可变对象

__iadd__ 的行为在可变对象和不可变对象之间有所不同。列表实现它以进行就地修改,而元组则没有它。

mutable_immutable.py
# List (mutable) example
lst = [1, 2, 3]
print(id(lst))  # Original ID
lst += [4, 5]
print(lst)      # [1, 2, 3, 4, 5]
print(id(lst))  # Same ID

# Tuple (immutable) example
tup = (1, 2, 3)
print(id(tup))  # Original ID
tup += (4, 5)
print(tup)      # (1, 2, 3, 4, 5)
print(id(tup))  # Different ID

列表就地修改自身,保持相同的 ID。元组创建一个新对象,因为它们是不可变的。这显示了 += 的行为差异。

对于不可变类型,当 __iadd__ 不可用时,Python 会回退到 __add__,创建一个新对象而不是就地修改。

自定义就地加法

我们可以为复杂对象实现自定义的就地加法逻辑。此示例展示了一个带有 += 运算的 Vector 类。

vector_iadd.py
class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __iadd__(self, other):
        if isinstance(other, Vector):
            self.x += other.x
            self.y += other.y
        else:
            self.x += other
            self.y += other
        return self
    
    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

v1 = Vector(1, 2)
v1 += Vector(3, 4)
print(v1)  # Vector(4, 6)

v1 += 5
print(v1)  # Vector(9, 11)

这个 Vector 类支持 += 运算,可操作 Vector 对象和标量。__iadd__ 方法处理这两种情况并修改实例。

该方法检查右操作数的类型,以确定是进行分量加法还是标量加法。它始终返回 self 以进行链式调用。

具有副作用的就地加法

__iadd__ 可以包含除了修改之外的副作用。此示例跟踪对象被修改的次数。

side_effects.py
class Counter:
    def __init__(self, value):
        self.value = value
        self.mod_count = 0
    
    def __iadd__(self, other):
        self.value += other
        self.mod_count += 1
        return self
    
    def __repr__(self):
        return f"Counter(value={self.value}, mods={self.mod_count})"

c = Counter(10)
c += 5
c += 3
print(c)  # Counter(value=18, mods=2)

这个 Counter 类在每次使用 += 时递增一个修改计数器。__iadd__ 方法更新值和计数器。

此模式对于审计更改或实施变更跟踪系统非常有用,在这些系统中,你需要知道对象被修改的频率。

继承和 __iadd__

当进行子类化时,你可能需要扩展 __iadd__ 的行为。此示例展示了如何在层次结构中正确实现它。

inheritance.py
class Base:
    def __init__(self, value):
        self.value = value
    
    def __iadd__(self, other):
        self.value += other
        return self

class Derived(Base):
    def __iadd__(self, other):
        print("Before addition:", self.value)
        super().__iadd__(other)
        print("After addition:", self.value)
        return self

d = Derived(10)
d += 5
# Output:
# Before addition: 10
# After addition: 15

Derived 类通过在仍然使用父类的加法逻辑的同时添加日志记录来扩展 += 行为。它调用 super() 进行委托。

此模式在添加新功能的同时维护父类的行为。该方法仍然返回 self 以支持正确的运算符链式调用。

最佳实践

资料来源

作者

我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直撰写编程文章。 至今,我已经撰写了超过 1,400 篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 Python 教程