ZetCode

Python __imul__ 方法

最后修改于 2025 年 4 月 8 日

本综合指南将探讨 Python 的 __imul__ 方法,即实现原地乘法的特殊方法。我们将介绍基本用法、运算符重载、可变与不可变类型以及实际示例。

基本定义

调用 __imul__ 方法来执行原地乘法运算 (*=)。在可能的情况下,它应修改并返回 self,但必要时也可以返回新对象。

关键特性:它在可能的情况下原地修改对象,返回结果(通常是 self),并由 *= 运算调用。如果未实现,Python 会回退到 __mul__,然后是赋值。

基本 __imul__ 实现

这是一个简单的实现,展示了 __imul__ 如何与自定义类一起工作。该方法修改对象的状态并返回自身。

basic_imul.py
class Number:
    def __init__(self, value):
        self.value = value
        
    def __imul__(self, other):
        self.value *= other
        return self
    
    def __repr__(self):
        return f"Number({self.value})"

num = Number(5)
num *= 3
print(num)  # Output: Number(15)

此示例展示了一个基本的 __imul__ 实现,该实现修改了实例的 value 属性。该方法返回 self 以允许链式操作。

*= 运算符调用 __imul__,后者原地更新对象的状态。这比创建新对象更有效。

带可变序列的 __imul__

对于列表等可变序列,__imul__ 执行原地重复。此示例演示了与自定义序列的行为。

sequence_imul.py
class MyList:
    def __init__(self, items):
        self.items = list(items)
        
    def __imul__(self, factor):
        self.items *= factor
        return self
    
    def __repr__(self):
        return f"MyList({self.items})"

lst = MyList([1, 2])
lst *= 3
print(lst)  # Output: MyList([1, 2, 1, 2, 1, 2])

这个自定义列表类实现了 __imul__ 来原地乘法其内容。原始对象被修改而不是创建新对象。

该实现委托给内置列表的 *= 操作,该操作有效地处理了重复。这种模式对于包装器来说很常见。

带不可变类型的 __imul__

不可变类型不能原地修改,因此它们的 __imul__ 必须返回新对象。此示例显示了行为差异。

immutable_imul.py
class ImmutableNumber:
    def __init__(self, value):
        self.value = value
        
    def __imul__(self, other):
        return ImmutableNumber(self.value * other)
    
    def __repr__(self):
        return f"ImmutableNumber({self.value})"

num = ImmutableNumber(5)
num *= 3
print(num)  # Output: ImmutableNumber(15)
print(id(num))  # Shows a new object was created

由于不可变对象无法更改其状态,因此 __imul__ 返回一个新实例。原始对象保持不变,并且变量被重新分配。

这与 Python 对元组等不可变类型的内置行为相匹配,在这些类型中,*= 会创建一个新对象而不是原地修改。

使用 __imul__ 进行矩阵乘法

对于矩阵等数学对象,__imul__ 可以实现原地矩阵乘法。此示例显示了一个简化版本。

matrix_imul.py
class Matrix:
    def __init__(self, data):
        self.data = data
        
    def __imul__(self, other):
        if isinstance(other, (int, float)):
            # Scalar multiplication
            self.data = [[x * other for x in row] 
                        for row in self.data]
            return self
        # Matrix multiplication would go here
        raise TypeError("Unsupported operand type")
    
    def __repr__(self):
        return f"Matrix({self.data})"

m = Matrix([[1, 2], [3, 4]])
m *= 2
print(m)  # Output: Matrix([[2, 4], [6, 8]])

这个矩阵类通过 __imul__ 实现原地标量乘法。该方法检查操作数类型并执行适当的操作。

对于实际的矩阵乘法,您需要实现完整的算法,但这展示了原地修改模式。

将 __imul__ 与其他操作结合使用

__imul__ 可以与其他操作结合使用以实现复杂行为。此示例显示了一个跟踪乘法历史的类。

tracking_imul.py
class TrackingNumber:
    def __init__(self, value):
        self.value = value
        self.history = []
        
    def __imul__(self, other):
        self.history.append((self.value, other))
        self.value *= other
        return self
    
    def get_history(self):
        return self.history
    
    def __repr__(self):
        return f"TrackingNumber({self.value})"

num = TrackingNumber(2)
num *= 3
num *= 4
print(num)  # Output: TrackingNumber(24)
print(num.get_history())  # Output: [(2, 3), (6, 4)]

这个类通过跟踪所有原地乘法来扩展基本的数字行为。历史记录存储在列表中,并在每次操作期间进行更新。

该示例演示了 __imul__ 如何维护除执行数学运算之外的附加状态。这对于调试或审计很有用。

最佳实践

资料来源

作者

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

列出所有 Python 教程