ZetCode

Python __isub__ 方法

最后修改于 2025 年 4 月 8 日

本综合指南探讨了 Python 的 __isub__ 方法,即用于就地减法的特殊方法。我们将涵盖基本用法、运算符重载、可变类型与不可变类型,以及实际示例。

基本定义

__isub__ 方法实现了就地减法操作 (-=)。它修改对象的值,而无需创建新对象。

主要特征:它应该修改并返回 self,当使用 -= 时被调用,并且通常为可变对象提供比常规减法更好的性能。

基本的 __isub__ 实现

这是一个简单的实现,展示了 __isub__ 如何与自定义类一起工作。该方法就地修改实例。

basic_isub.py
class Counter:
    def __init__(self, value):
        self.value = value
    
    def __isub__(self, other):
        self.value -= other
        return self
    
    def __repr__(self):
        return f"Counter({self.value})"

c = Counter(10)
c -= 3
print(c)  # Counter(7)

此示例展示了一个简单的计数器类,该类实现了就地减法。 当使用 -= 时,__isub__ 修改实例的值并返回 self。

该方法必须返回 self 才能正确地使用链式操作,并符合 Python 对就地操作的预期行为。

__isub__ 与可变对象

对于可变对象,__isub__ 可以通过避免在减法期间创建新对象来提供显著的性能优势。

mutable_isub.py
class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __isub__(self, other):
        self.x -= other.x
        self.y -= other.y
        return self
    
    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

v1 = Vector(5, 7)
v2 = Vector(2, 3)
v1 -= v2
print(v1)  # Vector(3, 4)

这个向量类实现了就地向量减法。该操作修改原始向量而不是创建一个新向量,这更有效率。

对于大型对象或频繁操作,与常规减法相比,这可以显著减少内存使用并提高性能。

__isub__ 与不可变对象

不可变对象无法就地修改,因此它们的 __isub__ 通常返回一个新对象,类似于常规减法。

immutable_isub.py
class ImmutablePoint:
    def __init__(self, x, y):
        self._x = x
        self._y = y
    
    @property
    def x(self):
        return self._x
    
    @property
    def y(self):
        return self._y
    
    def __isub__(self, other):
        return ImmutablePoint(self.x - other, self.y - other)
    
    def __repr__(self):
        return f"Point({self.x}, {self.y})"

p = ImmutablePoint(8, 6)
p -= 2
print(p)  # Point(6, 4)

当调用 __isub__ 时,这个不可变的点类返回一个新的实例。原始对象保持不变,保持了不可变性。

请注意,对于不可变对象,a -= b 的行为与 a = a - b 相同,只是可能具有不同的实现细节。

__isub__ 与不同类型

__isub__ 可以处理不同类型的操作,提供灵活的就地减法行为。

type_handling_isub.py
class Measurement:
    def __init__(self, value, unit='m'):
        self.value = value
        self.unit = unit
    
    def __isub__(self, other):
        if isinstance(other, (int, float)):
            return Measurement(self.value - other, self.unit)
        elif isinstance(other, Measurement):
            if self.unit == other.unit:
                return Measurement(self.value - other.value, self.unit)
            raise ValueError("Units must match")
        raise TypeError("Unsupported type")
    
    def __repr__(self):
        return f"{self.value}{self.unit}"

m = Measurement(10)
m -= 2.5
print(m)  # 7.5m

这个测量类处理数字和其他测量的减法。 它包括在减去测量值时进行单位检查。

该实现展示了如何在维护适当的错误检查和类型安全的同时,使 __isub__ 适用于多种类型。

__isub__ 在内置类型中

Python 的内置类型(如列表)实现了用于就地操作的 __isub__。 这是它与列表一起工作的方式。

builtin_isub.py
numbers = [1, 2, 3, 4, 5]
numbers -= [3, 4]  # Equivalent to numbers.extend([3, 4])
print(numbers)  # [1, 2, 3, 4, 5, 3, 4]

# For sets, -= performs difference_update
s = {1, 2, 3, 4, 5}
s -= {3, 4}
print(s)  # {1, 2, 5}

对于列表,-= 执行扩展操作(这可能会令人惊讶)。 对于集合,它执行差异更新,删除元素。

这演示了不同的内置类型如何根据其语义以不同的方式实现 __isub__。 始终检查类型的文档。

最佳实践

资料来源

作者

我的名字是 Jan Bodnar,我是一位充满激情的程序员,拥有丰富的编程经验。 我从 2007 年开始撰写编程文章。 迄今为止,我已经撰写了 1,400 多篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 Python 教程