I have a question to NumPy experts. Consider a NumPy scalar: c = np.arange(3.0).sum()
. If I try to multiply it with a custom sequence like e.g.
class S:
def __init__(self, lst):
self.lst = lst
def __len__(self):
return len(self.lst)
def __getitem__(self, s):
return self.lst[s]
c = np.arange(3.0).sum()
s = S([1, 2, 3])
print(c * s)
it works and I get: array([3., 6., 9.])
.
However, I can't do so with a list. For instance, if I inherit S from list and try it, this does not work anymore
class S(list):
def __init__(self, lst):
self.lst = lst
def __len__(self):
return len(self.lst)
def __getitem__(self, s):
return self.lst[s]
c = np.arange(3.0).sum()
s = S([1, 2, 3])
print(c * s)
and I get "can't multiply sequence by non-int of type 'numpy.float64'".
So how does NumPy distinguish between the two cases?
I am asking because I want to prevent such behavior for my "S" class without inheriting from list.
UPD
Since the question has been misunderstood a few times, I try to stress more exactly what the problem is. It's not about why the list does not cope with multiplication by float and the error is raised.
It is about why in the first case (when S is NOT inherited from list) the multiplication is performed by the object "c" and in the second case (when S is inherited from list) the multiplication is delegated to "s".
Apparently, the method c.__mul__
does some checks which pass in the first case, but fail in the second case, so that s.__rmul__
is called. The question is essentially: What are those checks? (I strongly doubt that this is anything like isinstance(other, list)
).
Best Answer
As pointed out by @hpaulj (thanks a lot!) this question was already around: Array and __rmul__ operator in Python Numpy.
The mechanics of the problem was explained very well in this answer: stackoverflow.com/a/38230576/901925. However, the proposed solution of inheriting from
np.ndarray
is certainly a "dirty" one.In the reply immediately after, a solution based on the function
__numpy_ufunc__
is proposed. The latter however is called__array_ufunc__
in modern NumPy. This function can be just set to None in the definition of the class "S". This leads to delegation of multiplcation tos.__rmul__
without attempts to perform it viac.__mul__
.