句法
object.__rmatmul__(self, other)
Python__rmatmul__() 方法实现了反向矩阵乘法 @ 操作,并对换了操作数。因此,当你调用x @ y 时,Python 会尝试调用 [x.__matmul__(y)](https://blog.finxter.com/python-__matmul__-magic-method/ "Python __matmul__() Magic Method").如果这个方法没有实现,Python 会尝试在右边的操作数上调用__rmatmul__ ,如果这个方法也没有实现,它会引发一个TypeError 。
我们称这种方法为*"* Dunder Method",即*"Double UnderscoreMethod"(也叫"Magic Method")*。
背景矩阵乘法
在下面的例子中,你创建了一个自定义类Data ,并覆盖了__matmul__() 方法,该方法只是返回一个假字符串。当然,真正的计算可能要复杂得多。
class Data:
def __matmul__(self, other):
return '... my result of matmul...'
a = Data()
b = Data()
c = a @ b
print(c)
# ... my result of matmul...
@ 操作符从3.5开始被引入Python的核心语法,这要感谢 PEP 465。它的唯一目标是解决矩阵乘法的问题。它甚至带有一个漂亮的助记符--@ 是 * 代表 mATrices。
不寻常的是,@ 被添加到核心 Python 语言中,而它只在某些库中使用。幸运的是,我们唯一一次使用@ 是用于装饰器函数。所以你不太可能会感到困惑。
Python的__matmul__与__rmatmul__对比
假设,你想在两个自定义对象x 和y 上计算@ 运算。
print(x @ y)
Python 首先尝试调用左边对象的__matmul__() 方法x.__matmul__(y) 。但这可能因为两个原因而失败。
- 方法
x.__matmul__()首先没有实现,或者 - 方法
x.__matmul__()已经实现,但是返回一个NotImplemented值,表明数据类型不兼容。
如果失败了,Python 试图通过调用y.__rmatmul__() 来解决这个问题,用于在右操作数上进行反向矩阵乘法 y 。
如果反向矩阵乘法被实现,Python知道它不会遇到非交换性操作的潜在问题。如果它只是执行y.__matmul__(x) 而不是x.__matmul__(y) ,结果将是错误的,因为当定义为自定义操作时,该操作可能是非交替的。这就是为什么需要y.__rmatmul__(x) 。
所以,x.__matmul__(y) 和x.__rmatmul__(y) 之间的区别是,前者计算x @ y ,而后者计算y @ x - 都是调用定义在对象x 上的各自方法。
一个很好的小技巧是在一个不支持矩阵乘法且不能改变的数据类型上间接定义矩阵乘法--比如像列表 这样的基本数据类型-- 通过在另一个可能控制的自定义类上实现 __rmatmul__ 。
你可以在这里看到这个效果,我们试图在左边的操作数x 上调用这个操作,但是由于它没有实现,Python 只是在右边的操作数y 上调用相反的操作。
class Data_1:
pass
class Data_2:
def __rmatmul__(self, other):
return 'called reverse matmul'
x = Data_1()
y = Data_2()
print(x @ y)
# called reverse matmul