学习Python __ipow__() 的魔法方法

126 阅读3分钟

语法

object.__ipow__(self, other)

Python__ipow__() 魔法实现了就地指数化 x **= y ,计算幂函数的结果x ** y ,并将其分配给第一个操作数的变量x 。这种类型的就地操作也被称为 增强的算术赋值.该方法只是返回要分配给第一个操作数的新值。

  • 当你调用x **= y 时,Python 首先尝试调用x.__ipow__(y)
  • 如果没有实现这一点,它会尝试正常的指数化操作 [x.__pow__(y)](https://blog.finxter.com/python-__pow__-magic-method/ "Python __pow__() Magic Method").
  • 如果这一点也没有实现,它将尝试反向指数化操作 [y.__rpow__(x)](https://blog.finxter.com/python-__rpow__-magic-method/ "Python __rpow__() Magic Method")与互换的操作数。

然后将结果赋值给第一个操作数x 。如果这些操作都没有实现,Python 会引发一个TypeError

我们把这称为 "Dunder Method",即*"Double UnderscoreMethod"(也称为"Magic Method")*。

重写__ipow__的基本例子

在下面的代码例子中,你创建了一个类Data ,并定义了魔法方法__ipow__(self, other)

  • "self"参数是每个方法的默认参数,它指的是被调用的对象--在我们的例子中,是就地操作的第一个操作数。
  • 原地方法的 "其他 "参数指的是第二个操作数,即原地操作中的y x **= y

该操作的返回值返回一个假的字符串'finxter 42' ,并分配给第一个操作数。在实践中,这将是就地指数化操作的结果。

class Data:
    def __ipow__(self, other):
        return 'finxter 42'


x = Data()
y = Data()

x **= y

print(x)
# finxter 42

就地指数化 **= 没有 __ipow__()

为了支持自定义类上的就地取材功能,你不需要覆盖__ipow__() 方法。因为如果该方法没有被定义,Python将回到正常的 [__pow__()](https://blog.finxter.com/python-__pow__-magic-method/ "Python __pow__() Magic Method")方法并将其结果分配给第一个操作数。

这里有一个例子:

class Data:
    def __pow__(self, other):
        return 'finxter 42'


x = Data()
y = Data()

x **= y

print(x)
# finxter 42

尽管没有定义__ipow__() 方法,但由于__pow__() "回退 "的神奇方法,就地指数化操作x **= y 仍然有效!

就地指数化 **= 没有 __ipow__() 和 __pow__()

要在一个自定义类上支持就地指数化x **= y ,你甚至不需要覆盖任何一个x.__ipow__(y)x.__pow__(y) 方法。如果这两个方法都没有定义,Python会返回到反向的 [y.__rpow__(x)](https://blog.finxter.com/python-__rpow__-magic-method/ "Python __rpow__() Magic Method")方法,并将其结果分配给第一个操作数。

这里有一个例子,你为第一个操作数创建一个不支持指数化操作的自定义类。然后你为第二个操作数定义一个自定义类,该类定义了__rpow__() 方法。对于原地操作,Python 返回到在第二操作数上定义的__rpow__() 方法,并将其分配给第一操作数x

class Data_1:
    pass

class Data_2:
    def __rpow__(self, other):
        return 'finxter 42'

x = Data_1()
y = Data_2()

x **= y

print(x)
# finxter 42

TypeError: **=的操作数类型不支持

如果你试图执行原地指数化x **= y ,但是既没有定义x.__ipow__(y) ,也没有定义x.__pow__(y) ,更没有定义y.__rpow(x) ,Python会引发一个"TypeError: unsupported operand type(s) for **=" 。要解决这个错误,只需在执行原地操作之前定义这些方法中的任何一个。

class Data:
    pass      # ... you should define __ipow__ here to prevent error! ... #


x = Data()
y = Data()

x **= y

输出:

Traceback (most recent call last):
  File "C:\Users\xcent\Desktop\code.py", line 8, in <module>
    x **= y
TypeError: unsupported operand type(s) for ** or pow(): 'Data' and 'Data'