使用你的类的__mul__覆盖其他__rmul__

6
在Python中,你的类的__rmul__方法是否可以覆盖另一个类的__mul__方法,而不对另一个类进行更改?
这个问题出现在我编写某种类型的线性算子类时,我希望它能够使用乘法语法来乘以numpy数组。以下是说明该问题的最小示例:
import numpy as np    

class AbstractMatrix(object):
    def __init__(self):
        self.data = np.array([[1, 2],[3, 4]])

    def __mul__(self, other):
        return np.dot(self.data, other)

    def __rmul__(self, other):
        return np.dot(other, self.data)

左乘运算很好用:

In[11]: A = AbstractMatrix()
In[12]: B = np.array([[4, 5],[6, 7]])
In[13]: A*B
Out[13]: 
array([[16, 19],
       [36, 43]])

但是右乘默认使用np.ndarray的版本,它将数组拆分并逐个元素执行乘法(这不是预期的结果):
In[14]: B*A
Out[14]: 
array([[array([[ 4,  8],
       [12, 16]]),
        array([[ 5, 10],
       [15, 20]])],
       [array([[ 6, 12],
       [18, 24]]),
        array([[ 7, 14],
       [21, 28]])]], dtype=object)

在这种情况下,我该如何使它调用我的类的__rmul__方法,以作用于原始(未拆分)数组?
欢迎回答特定于numpy数组的情况,但我也对覆盖无法修改的第三方类的方法的一般想法感兴趣。

1
为什么不使用@运算符? - Francisco
https://dev59.com/B2435IYBdhLWcg3w1z4t#5182501 - wwii
1
我相信唯一的情况是在右侧对象是左侧对象的子类时,__rXXX__方法才会在通常的__XXX__方法之前被检查。我对numpy内部不够了解,不知道它们是否可以被子类化。 - jasonharper
从文档中(https://docs.python.org/3/reference/datamodel.html#object.__rmul__)- 仅当左操作数不支持相应的操作且操作数类型不同时,才调用这些函数。 - wwii
1
作为一名从事数值分析的人,@操作符看起来非常棒!这是Python社区做出的一个很好的决定。我正在使用Python 2.7.6,但这个新特性可能会让我转向3.x版本。 - Nick Alger
显示剩余2条评论
1个回答

5

NumPy 尊重你的 __rmul__ 方法最简单的方法是设置 __array_priority__:

class AbstractMatrix(object):
    def __init__(self):
        self.data = np.array([[1, 2],[3, 4]])

    def __mul__(self, other):
        return np.dot(self.data, other)

    def __rmul__(self, other):
        return np.dot(other, self.data)

    __array_priority__ = 10000

A = AbstractMatrix()
B = np.array([[4, 5],[6, 7]])

这个像预期的那样工作。

>>> B*A
array([[19, 28],
       [27, 40]])

问题在于NumPy不遵循Python的“Numeric”数据模型。如果一个numpy数组是第一个参数,并且numpy.ndarray.__mul__不可行,那么它会尝试类似以下的操作:
result = np.empty(B.shape, dtype=object)
for idx, item in np.ndenumerate(B):
    result[idx] = A.__rmul__(item)

然而,如果第二个参数具有__array_priority__属性,并且它比第一个参数的优先级更高,那么它才会真正被使用:

A.__rmul__(B)

然而自从Python 3.5 (PEP-465)起,有一个@ (__matmul__) 运算符可以使用矩阵乘法:

>>> A = np.array([[1, 2],[3, 4]])
>>> B = np.array([[4, 5],[6, 7]])
>>> B @ A
array([[19, 28],
       [27, 40]])

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接