views:

302

answers:

2

I am trying to derive a class from a python primitive, the float, for the purpose of printing a different repr string when it's printed out.

How do I access the underlying data from the derived class when I do this?

Here's a simplified example of what I am trying to do:

class efloat(float):
    def __repr__(self):
        return "here's my number: %s" % str(WHAT CAN I PUT HERE???)

Ok, thanks folks! I think I get it now. Here's the finished class for anyone who's curious:

import math

class efloat(float):
    """efloat(x) -> floating point number with engineering representation when printed
       Convert a string or a number to a floating point number, if possible.
       When asked to render itself for printing (via str() or print) it is normalized
       to engineering style notation at powers of 10 in multiples of 3
       (for micro, milli, kilo, mega, giga, etc.)        
    """

    def _exponent(self):   
        if self == 0.0:
           ret = 0
        else:
           ret = math.floor(math.log10(abs(self)))
        return ret

    def _mantissa(self):
        return self/math.pow(10, self._exponent())

    def _asEng(self):
        shift = self._exponent() % 3

        retval = "%3.12ge%+d" % (self._mantissa()*math.pow(10, shift), self._exponent() - shift)
        return retval

    def __str__(self):
        return self._asEng()

    def __repr__(self):
        return str(self)

    def __add__(self, x):
        return efloat(float.__add__(self, float(x)))

    def __radd__(self, x):
        return efloat(float.__add__(self, float(x)))

    def __mul__(self, x):
        return efloat(float.__mul__(self, float(x)))

    def __rmul__(self, x):
        return efloat(float.__mul__(self, float(x)))

    def __sub__(self, x):
        return efloat(float.__sub__(self, float(x)))

    def __rsub__(self, x):
        return efloat(float.__rsub__(self, float(x)))

    def __div__(self, x):
        return efloat(float.__div__(self, float(x)))

    def __rdiv__(self, x):
        return efloat(float.__rdiv__(self, float(x)))

    def __truediv__(self, x):
        return efloat(float.__truediv__(self, float(x)))

    def __rtruediv__(self, x):
        return efloat(float.__rtruediv__(self, float(x)))

    def __pow__(self, x):
        return efloat(float.__pow__(self, float(x)))

    def __rpow__(self, x):
        return efloat(float.__rpow__(self, float(x)))

    def __divmod__(self, x):
        return efloat(float.__divmod__(self, float(x)))

    def __neg__(self):
        return efloat(float.__neg__(self))

    def __floordiv__(self, x):
        return efloat(float.__floordiv__(self, float(x)))
+4  A: 

If you don't override __str__, that will still access the underlying method, so:

class efloat(float):
    def __repr__(self):
        return "here's my number: %s" % self

will work. More generally, you could use self+0, self*1, or any other identity manipulation that you did not explicitly override; if you overrode them all, worst case, float.__add__(self, 0) or the like.

Alex Martelli
Thanks, that makes it work. So "self" actually refers to the underlying IEEE754 floating point number because I didn't do anything to muck with that?
K. Brafford
`self` refers to "this object": any method you didn't override go right to the parent class. I also showed how to explicitly ask for the parent class's implementation in case you HAD overridden everything!-)
Alex Martelli
`%s` implicitely calls `str`. So you're doing `% str(self)`, which will be the standard float.__str__. You can get back to a normal float by calling `float`, e.g.: `e = efloat(9.3); f = float(e); print type(f)`
John Fouhy
Ok, I thought I applied the lesson correctly...what did I do to my class!? (see the edited OP).What I am trying to make is a printable float that knows about engineering notation (just ask any engineer who's tried to use excel to do engineering notation and you'll realize what I am trying to do).Why does the efloat(1.0) + 1.0 type of usage go to infinite recursion?
K. Brafford
Do I need to re ask it as a new question?
K. Brafford
As you overrode `__add__`, the code in it that goes `self + efloat(x)` can only recurse -- that `+` is exactly `self.__add__`. Reread what I said above: **any method you didn't override** -- but now you overrode this one so you need a different technique, that I also showed. If not clear yet, please do ask a separate question, as this comment is already pretty big and crowded;-).
Alex Martelli
I get it now...thanks!
K. Brafford
+2  A: 

You can call the base class methods, by accessing them off the base class to get an unbound method and call them with self:

class myfloat(float):
    def __str__(self):
        return "My float is " + float.__str__(self)

print(myfloat(4.5))
Ants Aasma
Thanks. I got it working now!
K. Brafford