ZetCode

Python __sub__ 方法

最后修改于 2025 年 4 月 8 日

本综合指南将探讨 Python 的 __sub__ 方法,该特殊方法实现了减法运算 (-)。我们将涵盖基本用法、向量数学、自定义行为和实际示例。

基本定义

__sub__ 方法被调用来实现减法运算符 (-)。当你写 x - y 时,Python 会尝试调用 x.__sub__(y)

主要特点:它必须接受两个参数(self 和 other),返回减法结果,并且可以被重写以自定义行为。它适用于内置类型和自定义类型。

基本 __sub__ 实现

这是一个简单的类,实现了 __sub__ 以演示同类型对象之间的基本减法行为。

basic_sub.py
class Number:
    def __init__(self, value):
        self.value = value
    
    def __sub__(self, other):
        if isinstance(other, Number):
            return Number(self.value - other.value)
        return NotImplemented

a = Number(10)
b = Number(3)
result = a - b
print(result.value)  # Output: 7

此示例展示了如何实现 Number 对象之间的基本减法。__sub__ 方法会检查另一个操作数是否也是 Number。

返回 NotImplemented 会告诉 Python 尝试执行反向运算(__rsub__),如果可用的话。这可以保持运算符的灵活性。

向量减法

__sub__ 方法常用于数学运算,如向量减法,即我们减去相应的分量。

vector_sub.py
class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __sub__(self, other):
        if isinstance(other, Vector):
            return Vector(self.x - other.x, self.y - other.y)
        return NotImplemented
    
    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

v1 = Vector(5, 7)
v2 = Vector(3, 2)
result = v1 - v2
print(result)  # Output: Vector(2, 5)

此 Vector 类实现了逐分量减法。__sub__ 方法创建并返回一个具有减去分量的新 Vector。

__repr__ 方法提供了向量的可读字符串表示形式,使调试和输出更加清晰。

减去不同类型

__sub__ 可以通过检查另一个操作数的类型并实现适当的行为来处理不同类型之间的运算。

mixed_sub.py
class Currency:
    def __init__(self, amount, currency_code):
        self.amount = amount
        self.currency_code = currency_code
    
    def __sub__(self, other):
        if isinstance(other, Currency):
            if self.currency_code != other.currency_code:
                raise ValueError("Cannot subtract different currencies")
            return Currency(self.amount - other.amount, self.currency_code)
        elif isinstance(other, (int, float)):
            return Currency(self.amount - other, self.currency_code)
        return NotImplemented
    
    def __repr__(self):
        return f"{self.amount} {self.currency_code}"

usd1 = Currency(100, "USD")
usd2 = Currency(30, "USD")
print(usd1 - usd2)  # 70 USD
print(usd1 - 15)    # 85 USD

此 Currency 类允许减去同类型 Currency 对象,并且还支持直接从金额中减去数字。

该方法包含类型检查和货币验证,以确保只执行有意义的运算。这使得该类更加健壮。

使用 __rsub__ 进行反向减法

当左操作数不支持与右操作数进行减法时,Python 会尝试使用 __rsub__ 执行反向运算。

reverse_sub.py
class Temperature:
    def __init__(self, value):
        self.value = value
    
    def __sub__(self, other):
        if isinstance(other, Temperature):
            return Temperature(self.value - other.value)
        return NotImplemented
    
    def __rsub__(self, other):
        if isinstance(other, (int, float)):
            return Temperature(other - self.value)
        return NotImplemented
    
    def __repr__(self):
        return f"{self.value}°C"

temp = Temperature(10)
result1 = temp - Temperature(3)  # Uses __sub__
result2 = 20 - temp              # Uses __rsub__
print(result1)  # 7°C
print(result2)  # 10°C

此示例同时展示了正常减法和反向减法。当 Python 遇到 20 - temp 时,它首先尝试 int.__sub__,但失败了。

然后它尝试 temp.__rsub__(20),这是成功的。这使得该类在与内置类型一起使用时更加灵活。

原地减法 __isub__

__isub__ 方法实现了原地减法 (-=),它会修改对象而不是创建新对象。

inplace_sub.py
class Account:
    def __init__(self, balance):
        self.balance = balance
    
    def __sub__(self, other):
        if isinstance(other, (int, float)):
            return Account(self.balance - other)
        return NotImplemented
    
    def __isub__(self, other):
        if isinstance(other, (int, float)):
            self.balance -= other
            return self
        return NotImplemented
    
    def __repr__(self):
        return f"Account(balance={self.balance})"

acc = Account(100)
acc -= 30  # Uses __isub__
print(acc)  # Account(balance=70)

__isub__ 方法直接修改对象的状态并返回 self。当你不需要新对象时,这比 __sub__ 更有效。

对于可变对象,实现 __isub__ 可以通过避免不必要的对象创建来提供更好的性能。

最佳实践

资料来源

作者

我叫 Jan Bodnar,我是一名充满激情的程序员,拥有丰富的编程经验。我自 2007 年以来一直在撰写编程文章。至今,我已撰写了超过 1400 篇文章和 8 本电子书。我在教授编程方面拥有超过十年的经验。

列出所有 Python 教程