Frage

I'm looking to roll my own simple object that can keep track of units for variables (maybe I'll be adding other attributes like tolerances too). Here is what I have so far:

class newVar():
    def __init__(self,value=0.0,units='unknown'):
        self.value=value
        self.units=units

    def __str__(self):
        return str(self.value) + '(' + self.units + ')'

    def __magicmethodIdontknow__(self): 
        return self.value


diameter=newVar(10.0,'m') #define diameter's value and units

print diameter #printing will print value followed by units

#intention is that I can still do ALL operations of the object
#and they will be performed on the self.value inside the object.

B=diameter*2 

Because I don't have the right magic method i get the following output

10.0(m)
Traceback (most recent call last):
  File "C:\Users\user\workspace\pineCar\src\sandBox.py", line 25, in <module>
     B=diameter*2 
TypeError: unsupported operand type(s) for *: 'instance' and 'int'

I guess i could override every magic method to just return self.value but that sounds wrong. Maybe I need a decorator?

Also, I know I can just call diameter.value but that seems repetative

War es hilfreich?

Lösung

I once tried to implement something similar myself, but never managed to finish it. One way is to implement a new class from scratch, which contains both the value and the units. But if you want to use it in calculations, you have to implement all the magic methods like __add__ and __mul__. An alternative is to sub-class float itself:

class FloatWithUnit(float):
    def __new__(cls, val, unit):
        return float.__new__(cls, val)
    def __init__(self, val, unit):
        self.unit = unit
    def __str__(self):  
        return '%g %s' % (self, self.unit)
    def __repr__(self):
        return self.__str__()

Subclassing float is apparently a bit tricky, so you have to implement __new__ in addition to __init__, see here for more discussion. When entering such an object on the command line, it shows its units:

In [2]: g = FloatWithUnit(9.81, 'm/s^2')

In [3]: g
Out[3]: 9.81 m/s^2

In [4]: type(g)
Out[4]: __main__.FloatWithUnit

But when used in caluations, it behaves like a normal float

In [5]: g2 = 2 * g

In [6]: g2
Out[6]: 19.62

In [7]: type(g2)
Out[7]: float

Andere Tipps

class newVar():

    def __init__(self,value=0.0,units='unknown'):
        self.value=value
        self.units=units

    def __repr__(self):
        return str(self.value) + '(' + self.units + ')'

    def __mul__(self, other):

        if hasattr(other, "value"):
            return self.value * other.value
        else:
            return self.value * other

diameter=newVar(10.0,'m')

print diameter 
print diameter*2

another possible solution would be to override the object float representation

class newVar():

    def __init__(self,value=0.0,units='unknown'):
        self.value=value
        self.units=units

    def __repr__(self):
        return str(self.value) + '(' + self.units + ')'

    def __float__(self):

        return self.value

diameter=newVar(10.0,'m')

print diameter 
print float(diameter)*2

In Python you can override many different operators, some of which are for emulating numeric types. You can find out more here.

In the case of multiplication, all you need to do is override __mul__(self, other) like so:

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

You can also override other numeric operations like __add__(self, other) (for addition), __sub__(self, other) etc.

On a somewhat unrelated note, you might also want to think about what you want the behaviour be if one wanted to multiply to diameters together or add a weight to a diameter.

UPDATE

Based on your comment, you are probably better off overriding the the object's float representation as also mentioned by John, though it's not as clean as overriding the mathematical operators:

def __float__(self):
    return self.value
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top