Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
570 views
in Technique[技术] by (71.8m points)

python - Overriding other __rmul__ with your class's __mul__

In Python, is it possible for your class's __rmul__ method to override another class's __mul__ method, without making changes to the other class?

This question arises since I'm writing a class for a certain type of linear operator, and I want it to be able to multiply numpy arrays using the multiplication syntax. Here is a minimal example illustrating the issue:

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)

Left multiplication works fine:

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

But right multiplication defaults to np.ndarray's version, which splits the array up and performs multiplication element-by-element (this not what is desired):

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)

In this situation, how can I make it call my own class's __rmul__ on the original (unsplit) array?

Answers addressing the specific case of numpy arrays are welcome but I am also interested in the general idea of overriding methods of another third party class that cannot be modified.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The easiest way to make NumPy respect your __rmul__ method is to set an __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]])

This works like expected.

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

The problem is that NumPy doesn't respect Pythons "Numeric" Data model. If a numpy array is the first argument and numpy.ndarray.__mul__ isn't possible then it tries something like:

result = np.empty(B.shape, dtype=object)
for idx, item in np.ndenumerate(B):
    result[idx] = A.__rmul__(item)

However if the second argument has an __array_priority__ and it's higher than the one of the first argument only then it really uses:

A.__rmul__(B)

However since Python 3.5 (PEP-465) there is the @ (__matmul__) operator that can utilize matrix multiplication:

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...