ZetCode

Python __mul__ 方法

最后修改于 2025 年 4 月 8 日

这份综合指南探讨了 Python 的 __mul__ 方法,这是一个用于实现乘法运算符重载的特殊方法。我们将涵盖基本用法、交换律运算、矩阵乘法和实际示例。

基本定义

__mul__ 方法被调用以实现乘法运算符 (*)。当你编写 x * y 时,Python 尝试调用 x.__mul__(y)

关键特性:它必须返回乘法的结果,可以在任何类中定义,并且应该处理类型检查。对于交换律运算,还应该实现 __rmul__

基本 __mul__ 实现

这是一个简单的实现,展示了如何为自定义类重载乘法运算符。 此示例创建一个 Vector 类。

basic_mul.py
class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __mul__(self, scalar):
        if isinstance(scalar, (int, float)):
            return Vector(self.x * scalar, self.y * scalar)
        return NotImplemented
    
    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

v = Vector(2, 3)
print(v * 3)  # Vector(6, 9)

此示例显示了向量与标量的乘法。 __mul__ 方法在执行乘法之前检查右操作数是否为数字。

返回 NotImplemented 告诉 Python 尝试其他方法,例如右操作数上的 __rmul__(如果可用)。

实现交换律乘法

为了处理左操作数不支持乘法但右操作数支持的情况,我们实现了 __rmul__

commutative.py
class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __mul__(self, scalar):
        if isinstance(scalar, (int, float)):
            return Vector(self.x * scalar, self.y * scalar)
        return NotImplemented
    
    def __rmul__(self, scalar):
        return self.__mul__(scalar)
    
    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

v = Vector(2, 3)
print(v * 3)   # Vector(6, 9)
print(3 * v)   # Vector(6, 9)

通过实现 __rmul__v * 33 * v 都能正确工作。 当左操作数不知道如何与右操作数相乘时,将调用 __rmul__

由于标量乘法满足交换律,因此该实现仅委托给 __mul__

使用 __matmul__ 进行矩阵乘法

Python 3.5+ 引入了用于矩阵乘法的 @ 运算符,通过 __matmul__ 实现。

matrix_mul.py
class Matrix:
    def __init__(self, data):
        self.data = data
    
    def __matmul__(self, other):
        if len(self.data[0]) != len(other.data):
            raise ValueError("Incompatible matrix dimensions")
        
        result = [[0] * len(other.data[0]) for _ in range(len(self.data))]
        for i in range(len(self.data)):
            for j in range(len(other.data[0])):
                for k in range(len(other.data)):
                    result[i][j] += self.data[i][k] * other.data[k][j]
        return Matrix(result)
    
    def __repr__(self):
        return '\n'.join([' '.join(map(str, row)) for row in self.data))

A = Matrix([[1, 2], [3, 4]])
B = Matrix([[5, 6], [7, 8]])
print(A @ B)

此示例使用 @ 运算符实现正确的矩阵乘法。 __matmul__ 方法执行行和列的点积。

该实现检查兼容的维度并创建一个包含结果的新矩阵。 这与使用 __mul__ 的元素级乘法不同。

元素级乘法

对于表示集合的类,你可能需要元素级乘法而不是矩阵乘法。

elementwise.py
class Vector:
    def __init__(self, *components):
        self.components = components
    
    def __mul__(self, other):
        if isinstance(other, Vector):
            if len(self.components) != len(other.components):
                raise ValueError("Vectors must be same length")
            return Vector(*[a * b for a, b in zip(self.components, other.components)])
        return NotImplemented
    
    def __repr__(self):
        return f"Vector{self.components}"

v1 = Vector(1, 2, 3)
v2 = Vector(4, 5, 6)
print(v1 * v2)  # Vector(4, 10, 18)

当两个操作数都是向量时,此实现执行元素级乘法。 它在相乘之前检查向量是否具有相同的长度。

结果是一个新向量,其中每个分量是输入向量中相应分量的乘积。

组合不同的类型

__mul__ 方法可以在适当的时候处理不同类型之间的运算。 这是一个带有单位的例子。

units.py
class Meter:
    def __init__(self, value):
        self.value = value
    
    def __mul__(self, other):
        if isinstance(other, (int, float)):
            return Meter(self.value * other)
        if isinstance(other, Meter):
            return self.value * other.value  # returns area in square meters
        return NotImplemented
    
    def __rmul__(self, other):
        return self.__mul__(other)
    
    def __repr__(self):
        return f"{self.value}m"

distance = Meter(5)
print(distance * 2)    # 10m
print(3 * distance)    # 15m
area = distance * Meter(4)
print(area)           # 20

此示例显示了基于右操作数类型的不同行为。 乘以一个数字会缩放距离,而乘以两个 Meter 实例会返回一个面积。

该实现演示了单个运算符如何在保持类型安全的同时,根据上下文具有不同的含义。

最佳实践

资料来源

作者

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

列出所有 Python 教程