In python, How to make right operand takes the priority (of __rmul__ method) when multiplying two different class?
class, numpy, operator-keyword, overriding, python
Solution
If you don't inhereit from float, but instead create a new type wrapping float (so float._ mul_(yourtype) does not work), rmul will do what you want. The wrapping will of course not be free, though... and you'll have to implement all operations you want the type to support.
class T(object):
def __init__(self, val):
self.val = val
def __mul__(self, x):
print("mul")
return T(self.val*x)
def __rmul__(self, x):
print("rmul")
return T(self.val*x)
def __repr__(self):
return str(self.val)
>>> t = T(2)
>>> t * 2
mul
4
>>> 2*t
rmul
4
Problem
I am trying to make the following thing work but without success: I defined my own type `Unit` (inherit from build-in type `float`) to implement algebra for quantities with units. It does things in the way that: ``` class Unit(float): """provide a simple unit converter for a given quantity""" def __new__(cls, unit, num=1.): return super(Unit, cls).__new__(cls, num) def __init__(self, unit, num=1.): """set up base unit""" self.unit = unit def __str__(self,): return '{:s} {:s}'.format(super(Unit, self).__str__(), self.unit) def __rmul__(self, other): print 'rmul: {:f}'.format(super(Unit, self).__rmul__(other)) return Unit(self.unit, super(Unit, self).__rmul__(other)) def to(self,target): fun_conv = _conv(self.unit, target) return Unit(target, num=fun_conv(self)) c = 3e8 * Unit('m/s') # this will 1) create a Unit instance with magnitude '1' and unit 'm/s', # 2) invoke __rmul__ to return a new instance with number 3e8 and unit 'm/s' to variable 'c' print c.to('km/s') # returns 3e5 km/s ``` However, this `__rmul__` is only invoked when `float` being the left operand. If I make something like this: ``` velocities = np.array([20, 10]) * Unit('m/s') ``` Then `Unit.__rmul__` will not be invoked, and the same `numpy ndarray` is returned since now `Unit('m/s')` was treated like a plain `float` with value 1.0 What I expect is: after `ndarray * Unit`, a function similar to `Unit.to` can be attacted to the instance of ndarray as a method as well as an attribute `unit`, so I can further call `ndarray.to` to return a copy (or modified version, if it could, for memory efficiency) of the original ndarray that associated with new values and unit. How do I proceed? According what I have known and searched, `__mul__` of the left operand will be of the prior during `*`, i.e., the interpretor checks `LO.__mul__()` first, if it fails, then goes to `RO.__rmul__()`. I don't quite want to override `numpy.ndarray.__mul__` because I really don't know how complicated it would be, and whether there would be a big mess in case that it breaks the rules that ndarray acting on other objects. And, actually I even cannot find where are the codes that defines `__mul__` for `ndarray`. I simply used `inspect.getsource(np.ndarray)` but without success. Why does it fail on this? the exception was barely an `IOError`. Thank you so much for your concern!