Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In python, How to make right operand takes the priority (of __rmul__ method) when multiplying two different class?

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!

like image 927
Jerry Ma Avatar asked Nov 03 '22 18:11

Jerry Ma


1 Answers

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
like image 200
Fredrik Håård Avatar answered Nov 08 '22 05:11

Fredrik Håård