ndarray.__mul__ is too gready?
Hi, Say, one defines a class A that does not like to have numpy arrays in the lefthandside of an operation, say *, but in the rhs it is expected. For an example, consider A as a huge customized matrix that defines only matrix * vector where vector can be numpy array and scalar * matrix where scalar is python float or int. Users may accidentally hit vector * matrix and this should raise an exception. But when vector is numpy array, then ndarray.__mul__ calls element * A for each array element and here A.__rmul__ gets only the types of array elements (that are floats, for instance) as rhs and hence is not able to decide that it should return NotImplemented. Instead, it computes scalar * matrix and the final result will be array([element1*matrix, element2*matrix, ..]) which is not desired already because of the memory usage and should have raised an exception instead. Here is a small example illustrating the problem: class A: def __rmul__(self, other): if isinstance(other, ndarray): return NotImplemented if isinstance(other, (int, float)): return self # pretend other==1 for an example return NotImplemented def __repr__(self): return 'A()'
array([1,2,3]) * A() array([A(), A(), A()], dtype=object) # expected TypeError
Is there any workaround to this problem? In other situations/applications the above behavior of ndarray.__mul__ is a feature but is there any way to avoid this feature? Thanks, Pearu
Just for the record, because I had a very similar problem: On Wednesday 23 January 2008 12:11, Pearu Peterson wrote:
Users may accidentally hit vector * matrix and this should raise an exception. But when vector is numpy array, then ndarray.__mul__ calls element * A for each array element and here A.__rmul__ gets only the types of array elements (that are floats, for instance) as rhs and hence is not able to decide that it should return NotImplemented. Instead, it computes scalar * matrix and the final result will be array([element1*matrix, element2*matrix, ..]) which is not desired already because of the memory usage and should have raised an exception instead.
Here is a small example illustrating the problem:
class A: def __rmul__(self, other): if isinstance(other, ndarray): return NotImplemented if isinstance(other, (int, float)): return self # pretend other==1 for an example return NotImplemented
def __repr__(self): return 'A()'
array([1,2,3]) * A()
array([A(), A(), A()], dtype=object) # expected TypeError
Is there any workaround to this problem? In other situations/applications the above behavior of ndarray.__mul__ is a feature but is there any way to avoid this feature?
The class attribute __array_priority__ seems to control what exactly happens when those special functions are called. It is said to be documented in Travis Oliphant's Numpy book. Writing __array_priority__ = 10.0 should do what you want. So, the class definition should look like this: class A(object): __array_priority__ = 10.0 def __rmul__(self, other): ... <smart code here> ...
Thanks, Pearu
Kind regards, Eike.
participants (2)

Eike Welk

Pearu Peterson