Home > Mobile >  How to subclass "float" without implementing its method?
How to subclass "float" without implementing its method?

Time:03-15

I want to subclass from float but don't want it to init soon. I also don't want to explicitly call float() for my object.

For example, I don't want to calculate anything before it is required. I want only to do an object that behaves like float. Here is how I want to create class:

class MassiveAverage(float):
    def __init__(self, floats: list[float]):
        self.floats = floats

    def __float__(self) -> float:
        return sum(self.floats) / len(self.floats)

And this is how I want to use it:

massive_average = MassiveAverage([1.1, 2.2])  # no any calculations
massive_sum = massive_average * 2  # this is were it calculates its float value

CodePudding user response:

you should overwrite the operator, for example, to overwrite *, you can overwrite the __mul__ method

def __mul__(self, float): ...  

see below for methods can be defined to emulate numeric objects https://docs.python.org/3/reference/datamodel.html?highlight=rmul#emulating-numeric-types

CodePudding user response:

__float__ is used for exactly one purpose: to define the behavior of float(x) as x.__float__(). There is no implicit conversion in an expression like massive_average * 2. This could mean any number of things:

massive_average.__int__() * 2
massive_average.__float__() * 2
massive_average.__complex__() * 2
massive_avarge.__str__() * 2

so Python refuses to guess. It will try massive_average.__mul__(2), and failing that, (2).__rmul__(massive_average), before giving up.

Each of the type-specific "conversion" methods are used only by the corresponding type itself. print, for example, does not call __str__ (directly); it only is defined to call str on each of its arguments, and str takes care of calling __str__.

CodePudding user response:

For the answer to this question I am going to assume you are already familiar with python's "magic methods". @gftea's answer has a link to the documentation for some of the magic methods if you are not familiar.

You are going to have to manually define each "magic function" __mul__, __add__, __sub__, etc.

class MassiveAverage:

    def __init__(self, floats):
        self._avg = sum(floats)/len(floats)

    def __mul__(self, other):
        return self._avg * other

    def __sub__(self, other):
        return self._avg - other
    
    def __add__(self, other):
        return self._avg   other

    ...

But, this doesn't handle your lazy evaluation use case. Instead, we could maintain an internal cache, and on the first time one of these magic methods are evaluated, we could run the average function.

class MassiveAverage:

    def __init__(self, floats):
        self._floats = floats
        self._avg = None

    @property
    def avg(self):
        if self._avg is None:
            return sum(self._floats) / len(self._floats)
        return self._avg

Then, we can replace our magic functions and use self._avg.

    def __mul__(self, other):
        return self.avg * other
    
    def __add__(self, other):
        return self.avg   other

    def __sub__(self, other):
        return self.avg - other
    ...

Unfortunately, you cannot subclass float in the manner you want. Because you are specifying lazy evaluation, you are fundamentally changing how the methods in the float class work (since they don't need lazy evaluation). You would still have to manually change each magic method.

  • Related