bia-fitness/bia/units.py

121 lines
3.6 KiB
Python
Raw Normal View History

2015-03-23 21:41:55 +00:00
from collections import defaultdict
2015-03-24 20:22:32 +00:00
_UNITS = {
# mass
'kg': {'base': 'kg', 'scale': 1.0},
'lb': {'base': 'kg', 'scale': 2.20462},
'g': {'base': 'kg', 'scale': 1000.0},
'm': {'base': 'm', 'scale': 1.0},
}
2015-03-23 21:41:55 +00:00
class UnitError(ValueError):
pass
class ConversionError(ValueError):
pass
class Unit(object):
2015-03-24 20:22:32 +00:00
def __init__(self, numerator, denominator=None):
if isinstance(numerator, str):
self.numerator = [numerator]
else:
self.numerator = numerator
self.denominator = [] if denominator is None else denominator
for u in self.numerator + self.denominator:
if u not in _UNITS:
raise ValueError('invalid unit {}'.format(self))
2015-03-23 21:41:55 +00:00
2015-03-24 20:22:32 +00:00
self._simplify()
2015-03-24 20:22:32 +00:00
def __str__(self):
return '*'.join(self.numerator)
def __repr__(self):
return 'Unit({!r}, {!r})'.format(self.numerator, self.denominator)
2015-03-24 20:22:32 +00:00
def __eq__(self, other):
return self.numerator == other.numerator and self.denominator == other.denominator
2015-03-24 20:22:32 +00:00
def __mul__(self, other):
return Unit(self.numerator + other.numerator, self.denominator + other.denominator)
2015-03-24 20:22:32 +00:00
def __div__(self, other):
return Unit(self.numerator + other.denominator, self.denominator + other.numerator)
def _simplify(self):
for u in list(self.denominator):
if u in self.numerator:
self.numerator.remove(u)
self.denominator.remove(u)
def conversion_factor(self, other):
factor = 1
for u in self.numerator:
factor /= _UNITS[u]['scale']
for u in other.numerator:
factor *= _UNITS[u]['scale']
return factor
@staticmethod
def unit(u):
if isinstance(u, Unit):
return u
else:
return Unit(u)
class UnitValue(object):
def __init__(self, n, unit):
self.scalar = float(n)
self.unit = Unit.unit(unit)
2015-03-23 21:41:55 +00:00
def as_unit(self, unit):
2015-03-24 20:22:32 +00:00
unit = Unit.unit(unit)
2015-03-23 21:41:55 +00:00
if self.unit == unit:
return self.__class__(self.scalar, self.unit)
2015-03-24 20:22:32 +00:00
return self.__class__(self.scalar * self.unit.conversion_factor(unit), unit)
2015-03-23 21:41:55 +00:00
def __str__(self):
return '{}{}'.format(self.scalar, self.unit)
def __repr__(self):
return 'U({}, {!r})'.format(self.scalar, self.unit)
2015-03-24 03:45:53 +00:00
def __cmp__(self, other):
if self.unit != other.unit:
other = other.as_unit(self.unit)
2015-03-24 04:07:01 +00:00
# cmp() removed in Python 3, recommended to replace with this
return (self.scalar > other.scalar) - (self.scalar < other.scalar)
2015-03-24 03:45:53 +00:00
def __add__(self, other):
if self.unit != other.unit:
other = other.as_unit(self.unit)
return self.__class__(self.scalar + other.scalar, self.unit)
2015-03-24 03:45:53 +00:00
def __sub__(self, other):
if self.unit != other.unit:
other = other.as_unit(self.unit)
return self.__class__(self.scalar - other.scalar, self.unit)
def __mul__(self, other):
2015-03-24 20:22:32 +00:00
if isinstance(other, UnitValue):
if self.unit != other.unit:
other = other.as_unit(self.unit)
2015-03-24 20:22:32 +00:00
return self.__class__(self.scalar * other.scalar, self.unit * other.unit)
else:
return self.__class__(self.scalar * other, self.unit)
def __div__(self, other):
2015-03-24 20:22:32 +00:00
if isinstance(other, UnitValue):
if self.unit != other.unit:
other = other.as_unit(self.unit)
2015-03-24 20:22:32 +00:00
return self.__class__(self.scalar / other.scalar, self.unit / other.unit)
else:
return self.__class__(self.scalar / other, self.unit)