270 lines
7.6 KiB
Python
270 lines
7.6 KiB
Python
|
"""UnitDbl module."""
|
||
|
|
||
|
import operator
|
||
|
|
||
|
|
||
|
class UnitDbl(object):
|
||
|
"""Class UnitDbl in development.
|
||
|
"""
|
||
|
# Unit conversion table. Small subset of the full one but enough
|
||
|
# to test the required functions. First field is a scale factor to
|
||
|
# convert the input units to the units of the second field. Only
|
||
|
# units in this table are allowed.
|
||
|
allowed = {
|
||
|
"m": (0.001, "km"),
|
||
|
"km": (1, "km"),
|
||
|
"mile": (1.609344, "km"),
|
||
|
|
||
|
"rad": (1, "rad"),
|
||
|
"deg": (1.745329251994330e-02, "rad"),
|
||
|
|
||
|
"sec": (1, "sec"),
|
||
|
"min": (60.0, "sec"),
|
||
|
"hour": (3600, "sec"),
|
||
|
}
|
||
|
|
||
|
_types = {
|
||
|
"km": "distance",
|
||
|
"rad": "angle",
|
||
|
"sec": "time",
|
||
|
}
|
||
|
|
||
|
def __init__(self, value, units):
|
||
|
"""Create a new UnitDbl object.
|
||
|
|
||
|
Units are internally converted to km, rad, and sec. The only
|
||
|
valid inputs for units are [m, km, mile, rad, deg, sec, min, hour].
|
||
|
|
||
|
The field UnitDbl.value will contain the converted value. Use
|
||
|
the convert() method to get a specific type of units back.
|
||
|
|
||
|
= ERROR CONDITIONS
|
||
|
- If the input units are not in the allowed list, an error is thrown.
|
||
|
|
||
|
= INPUT VARIABLES
|
||
|
- value The numeric value of the UnitDbl.
|
||
|
- units The string name of the units the value is in.
|
||
|
"""
|
||
|
self.checkUnits(units)
|
||
|
|
||
|
data = self.allowed[units]
|
||
|
self._value = float(value * data[0])
|
||
|
self._units = data[1]
|
||
|
|
||
|
def convert(self, units):
|
||
|
"""Convert the UnitDbl to a specific set of units.
|
||
|
|
||
|
= ERROR CONDITIONS
|
||
|
- If the input units are not in the allowed list, an error is thrown.
|
||
|
|
||
|
= INPUT VARIABLES
|
||
|
- units The string name of the units to convert to.
|
||
|
|
||
|
= RETURN VALUE
|
||
|
- Returns the value of the UnitDbl in the requested units as a floating
|
||
|
point number.
|
||
|
"""
|
||
|
if self._units == units:
|
||
|
return self._value
|
||
|
|
||
|
self.checkUnits(units)
|
||
|
|
||
|
data = self.allowed[units]
|
||
|
if self._units != data[1]:
|
||
|
msg = "Error trying to convert to different units.\n" \
|
||
|
" Invalid conversion requested.\n" \
|
||
|
" UnitDbl: %s\n" \
|
||
|
" Units: %s\n" % (str(self), units)
|
||
|
raise ValueError(msg)
|
||
|
|
||
|
return self._value / data[0]
|
||
|
|
||
|
def __abs__(self):
|
||
|
"""Return the absolute value of this UnitDbl."""
|
||
|
return UnitDbl(abs(self._value), self._units)
|
||
|
|
||
|
def __neg__(self):
|
||
|
"""Return the negative value of this UnitDbl."""
|
||
|
return UnitDbl(-self._value, self._units)
|
||
|
|
||
|
def __bool__(self):
|
||
|
"""Return the truth value of a UnitDbl."""
|
||
|
return bool(self._value)
|
||
|
|
||
|
def __eq__(self, rhs):
|
||
|
return self._cmp(rhs, operator.eq)
|
||
|
|
||
|
def __ne__(self, rhs):
|
||
|
return self._cmp(rhs, operator.ne)
|
||
|
|
||
|
def __lt__(self, rhs):
|
||
|
return self._cmp(rhs, operator.lt)
|
||
|
|
||
|
def __le__(self, rhs):
|
||
|
return self._cmp(rhs, operator.le)
|
||
|
|
||
|
def __gt__(self, rhs):
|
||
|
return self._cmp(rhs, operator.gt)
|
||
|
|
||
|
def __ge__(self, rhs):
|
||
|
return self._cmp(rhs, operator.ge)
|
||
|
|
||
|
def _cmp(self, rhs, op):
|
||
|
"""Compare two UnitDbl's.
|
||
|
|
||
|
= ERROR CONDITIONS
|
||
|
- If the input rhs units are not the same as our units,
|
||
|
an error is thrown.
|
||
|
|
||
|
= INPUT VARIABLES
|
||
|
- rhs The UnitDbl to compare against.
|
||
|
- op The function to do the comparison
|
||
|
|
||
|
= RETURN VALUE
|
||
|
- Returns op(self, rhs)
|
||
|
"""
|
||
|
self.checkSameUnits(rhs, "compare")
|
||
|
return op(self._value, rhs._value)
|
||
|
|
||
|
def __add__(self, rhs):
|
||
|
"""Add two UnitDbl's.
|
||
|
|
||
|
= ERROR CONDITIONS
|
||
|
- If the input rhs units are not the same as our units,
|
||
|
an error is thrown.
|
||
|
|
||
|
= INPUT VARIABLES
|
||
|
- rhs The UnitDbl to add.
|
||
|
|
||
|
= RETURN VALUE
|
||
|
- Returns the sum of ourselves and the input UnitDbl.
|
||
|
"""
|
||
|
self.checkSameUnits(rhs, "add")
|
||
|
return UnitDbl(self._value + rhs._value, self._units)
|
||
|
|
||
|
def __sub__(self, rhs):
|
||
|
"""Subtract two UnitDbl's.
|
||
|
|
||
|
= ERROR CONDITIONS
|
||
|
- If the input rhs units are not the same as our units,
|
||
|
an error is thrown.
|
||
|
|
||
|
= INPUT VARIABLES
|
||
|
- rhs The UnitDbl to subtract.
|
||
|
|
||
|
= RETURN VALUE
|
||
|
- Returns the difference of ourselves and the input UnitDbl.
|
||
|
"""
|
||
|
self.checkSameUnits(rhs, "subtract")
|
||
|
return UnitDbl(self._value - rhs._value, self._units)
|
||
|
|
||
|
def __mul__(self, rhs):
|
||
|
"""Scale a UnitDbl by a value.
|
||
|
|
||
|
= INPUT VARIABLES
|
||
|
- rhs The scalar to multiply by.
|
||
|
|
||
|
= RETURN VALUE
|
||
|
- Returns the scaled UnitDbl.
|
||
|
"""
|
||
|
return UnitDbl(self._value * rhs, self._units)
|
||
|
|
||
|
def __rmul__(self, lhs):
|
||
|
"""Scale a UnitDbl by a value.
|
||
|
|
||
|
= INPUT VARIABLES
|
||
|
- lhs The scalar to multiply by.
|
||
|
|
||
|
= RETURN VALUE
|
||
|
- Returns the scaled UnitDbl.
|
||
|
"""
|
||
|
return UnitDbl(self._value * lhs, self._units)
|
||
|
|
||
|
def __div__(self, rhs):
|
||
|
"""Divide a UnitDbl by a value.
|
||
|
|
||
|
= INPUT VARIABLES
|
||
|
- rhs The scalar to divide by.
|
||
|
|
||
|
= RETURN VALUE
|
||
|
- Returns the scaled UnitDbl.
|
||
|
"""
|
||
|
return UnitDbl(self._value / rhs, self._units)
|
||
|
|
||
|
def __str__(self):
|
||
|
"""Print the UnitDbl."""
|
||
|
return "%g *%s" % (self._value, self._units)
|
||
|
|
||
|
def __repr__(self):
|
||
|
"""Print the UnitDbl."""
|
||
|
return "UnitDbl(%g, '%s')" % (self._value, self._units)
|
||
|
|
||
|
def type(self):
|
||
|
"""Return the type of UnitDbl data."""
|
||
|
return self._types[self._units]
|
||
|
|
||
|
def range(start, stop, step=None):
|
||
|
"""Generate a range of UnitDbl objects.
|
||
|
|
||
|
Similar to the Python range() method. Returns the range [
|
||
|
start, stop) at the requested step. Each element will be a
|
||
|
UnitDbl object.
|
||
|
|
||
|
= INPUT VARIABLES
|
||
|
- start The starting value of the range.
|
||
|
- stop The stop value of the range.
|
||
|
- step Optional step to use. If set to None, then a UnitDbl of
|
||
|
value 1 w/ the units of the start is used.
|
||
|
|
||
|
= RETURN VALUE
|
||
|
- Returns a list containing the requested UnitDbl values.
|
||
|
"""
|
||
|
if step is None:
|
||
|
step = UnitDbl(1, start._units)
|
||
|
|
||
|
elems = []
|
||
|
|
||
|
i = 0
|
||
|
while True:
|
||
|
d = start + i * step
|
||
|
if d >= stop:
|
||
|
break
|
||
|
|
||
|
elems.append(d)
|
||
|
i += 1
|
||
|
|
||
|
return elems
|
||
|
|
||
|
range = staticmethod(range)
|
||
|
|
||
|
def checkUnits(self, units):
|
||
|
"""Check to see if some units are valid.
|
||
|
|
||
|
= ERROR CONDITIONS
|
||
|
- If the input units are not in the allowed list, an error is thrown.
|
||
|
|
||
|
= INPUT VARIABLES
|
||
|
- units The string name of the units to check.
|
||
|
"""
|
||
|
if units not in self.allowed:
|
||
|
raise ValueError("Input units '%s' are not one of the supported "
|
||
|
"types of %s" % (
|
||
|
units, list(self.allowed.keys())))
|
||
|
|
||
|
def checkSameUnits(self, rhs, func):
|
||
|
"""Check to see if units are the same.
|
||
|
|
||
|
= ERROR CONDITIONS
|
||
|
- If the units of the rhs UnitDbl are not the same as our units,
|
||
|
an error is thrown.
|
||
|
|
||
|
= INPUT VARIABLES
|
||
|
- rhs The UnitDbl to check for the same units
|
||
|
- func The name of the function doing the check.
|
||
|
"""
|
||
|
if self._units != rhs._units:
|
||
|
msg = "Cannot %s units of different types.\n" \
|
||
|
"LHS: %s\n" \
|
||
|
"RHS: %s" % (func, self._units, rhs._units)
|
||
|
raise ValueError(msg)
|