Deja tu comentario. Write your comment here

No se pueden hacer enlaces, el antispam no aceptará el comentario. Do not create any link, the antispammer will not accept the comment.



Pythonic unit conversion

Python

Unit conversion is one of the tasks in engineering computations that suck the most. It's just tedious and it is a source for million of stupid bugs. I'm writing software for a company and I was feeling the units pain so hard. I decided to face the problem and to build the ultimate unit conversion tool. And y pythonic one!

All the efforts I've found on the Internet define an intermediate class that emulates float. The obvious question is: if a measure is a float... Why not subclassing float? The answer is as easy as the fact that subclassing a built-in type in python is not that easy (what a sentence, my brain hurts). At least it reaches the proficient level in python that just a small portion of scientists have. All the fortran programmers (like me) get in serious trouble when deal with object orientation. Even more when the things get messy with class methods, instance methods, properties and decorators. I hardly understand classes and I've found that I didn't understand the python's new style classes at all (this is a problem given that in Python 3.0 there will only be new style classes).

The problem is the following. If you subclass float and define an instance method that returns self, as an instance method it does not return the derived class but the parent, in this case float. If we want the instance to behave like a factory of itself you must define a classmethod. The following code does the trick, all the explanations are in the documentation.

from constants import *

class Temperature(float):
    """
    Class that models a temperature measure with conversion utilities

    Supported units are

    * Kelvin

    * Celsius

    * Fahrenheit

    Normal instantiation is a temperature in Kelvin

    >>> T = Temperature(100)
    >>> T
    100.0

    But you can instantiate and specify if unit is Celsius or
    Fahrenheit

    >>> T = Temperature(100).unit('F')
    >>> T
    310.92777777777775

    Unit conversion is as easy as it gets.

    >>> T.C
    37.777777777777771
    >>> T.F
    99.999999999999986

    You can compute with temperatures because inherits from the float
    built-in

    >>> T1 = Temperature(200)
    >>> T2 = Temperature(0).unit('C')
    >>> T1+T2
    473.14999999999998

    If you don't want to use the class' attribute you can use the
    function `getattr` to get a value using the unit code.

    >>> getattr(T,'C')
    37.777777777777771
    """
    def __init__(self,data):
        float.__init__(self,float(data))
        self.data = float(data)

    @classmethod
    def factory(cls,data):
        """
        This factory makes that any returned value is a measure
        instead of a float.
        """
        return cls(data)

    def unit(self,units='K'):
        if units == 'K':
            return self.factory(self.data)
        elif units == 'C':
            return self.factory(C2K(self.data))
        elif units == 'F':
            return self.factory(F2K(self.data))
        else:
            raise ValueError("Wrong temperature input code")
        
    @property
    def C(self):
        return self.factory(K2C(self.data))

    @property
    def F(self):
        return self.factory(K2F(self.data))

This way is much faster because it avoids unnecessary method calls albeit being more pythonic.

Por guillem  |  en: vie 09 Ene 2009

Comentarios