ZetCode

Python __ipow__ 方法

最后修改于 2025 年 4 月 8 日

本综合指南探讨了 Python 的 __ipow__ 方法,它是用于原地求幂的特殊方法。我们将涵盖基本用法、数值运算、运算符重载和实际示例。

基本定义

__ipow__ 方法实现了原地求幂运算 (**=)。它应该原地修改对象并返回结果。

主要特点:它直接修改对象的状态,返回 self(除非是不可变的),并且为 **= 运算调用。如果未实现,Python 将回退到 __pow__

基本的 __ipow__ 实现

这是一个简单的实现,展示了 __ipow__ 如何与数字类型一起工作。该方法修改对象并返回自身。

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

num = Number(2)
num **= 3
print(num)  # Number(8)

此示例展示了一个基本的 __ipow__ 实现。该方法将存储的值提高到给定的幂,并返回修改后的对象。

当可用时,**= 运算符会调用 __ipow__,与创建新对象相比,提供更有效的原地运算。

__ipow__ 与自定义矩阵类

对于矩阵运算,__ipow__ 可以提供有效的原地矩阵求幂,避免创建临时对象。

matrix_ipow.py
class Matrix:
    def __init__(self, data):
        self.data = data
    
    def __ipow__(self, power):
        if not isinstance(power, int) or power < 0:
            raise ValueError("Power must be non-negative integer")
        
        result = [[1 if i == j else 0 for j in range(len(self.data))]
                 for i in range(len(self.data))]
        
        for _ in range(power):
            result = [[sum(a*b for a,b in zip(row, col))
                      for col in zip(*self.data)] for row in result]
        
        self.data = result
        return self
    
    def __repr__(self):
        return '\n'.join(' '.join(map(str, row)) for row in self.data)

m = Matrix([[1, 1], [1, 0]])
m **= 3
print(m)

此矩阵类实现了原地矩阵求幂。__ipow__ 方法在一个循环中执行矩阵乘法,更新内部数据。

该实现检查有效的幂值,并使用列表推导式进行高效的矩阵运算。这避免了创建中间矩阵对象。

不可变对象和 __ipow__

对于不可变对象,__ipow__ 应该返回一个新对象,而不是修改现有的对象,类似于元组处理原地运算的方式。

immutable_ipow.py
class ImmutableNumber:
    def __init__(self, value):
        self._value = value
    
    def __ipow__(self, other):
        return ImmutableNumber(self._value ** other)
    
    def __pow__(self, other):
        return ImmutableNumber(self._value ** other)
    
    @property
    def value(self):
        return self._value
    
    def __repr__(self):
        return f"ImmutableNumber({self.value})"

num = ImmutableNumber(3)
num **= 4
print(num)  # ImmutableNumber(81)

这个不可变的数字类从 __ipow__ 返回一个新的实例,而不是修改自身。**= 运算符重新绑定变量。

请注意,为了保持一致性,__ipow____pow__ 都已实现。不可变的性质意味着原地运算不是真正的原地运算。

带模数参数的 __ipow__

__ipow__ 方法可以选择性地支持模数参数,类似于带有三个参数的内置 pow() 函数。

modulo_ipow.py
class ModNumber:
    def __init__(self, value):
        self.value = value
    
    def __ipow__(self, args):
        if isinstance(args, tuple):
            power, mod = args
            self.value = pow(self.value, power, mod)
        else:
            self.value **= args
        return self
    
    def __repr__(self):
        return f"ModNumber({self.value})"

num = ModNumber(3)
num **= 4
print(num)  # ModNumber(81)

num = ModNumber(3)
num **= (4, 10)  # 3^4 mod 10
print(num)  # ModNumber(1)

此实现检查参数是否为元组(用于模数运算)或单个值(常规求幂)。它使用 Python 的内置 pow()

模数参数对于密码学运算和模算术非常有用,在这些运算中,中间结果需要保持在界限内。

回退到 __pow__ 行为

当未实现 __ipow__ 时,Python 会回退到 __pow__ 和常规赋值。此示例演示了差异。

fallback_ipow.py
class PowOnly:
    def __init__(self, value):
        self.value = value
    
    def __pow__(self, other):
        return PowOnly(self.value ** other)
    
    def __repr__(self):
        return f"PowOnly({self.value})"

class IPow(PowOnly):
    def __ipow__(self, other):
        self.value **= other
        return self

a = PowOnly(2)
b = PowOnly(2)

a **= 3
print(a)  # Shows new object id
print(a is b)  # False

x = IPow(2)
y = x

x **= 3
print(x is y)  # True, same object modified

PowOnly 类在每次运算时创建新对象,而 IPow 修改现有对象。is 检查证明了这种差异。

在设计应有效支持原地运算的类时,了解这种回退行为非常重要。

最佳实践

资料来源

作者

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

列出所有 Python 教程