Source code for siquant.units

from functools import total_ordering

from .dimensions import SIDimensions, dim_div, dim_mul, dim_pow, dim_str
from .util import immutable, flyweight
from .exceptions import UnitMismatchError


[docs]@total_ordering @flyweight @immutable class SIUnit: """SIUnit is a scaling of SI base unit dimensions. :cvar factory: :vartype factory: ``Callable[[_T, SIUnit], _Q]`` :param scale: The scaling factor of base SI dimensions. :type scale: ``numbers.Real`` :param dimensions: The base SI dimensions. :type dimensions: ``tuple`` """ __slots__ = ("scale", "dimensions", "__weakref__") #: #: The factory function which unit instances use to create quantities. #: #: .. note:: #: #: SIUnit.factory is mapped to :class:`~siquant.quantities.Quantity` #: in __init__ by default. However, it is *not* required to be a type, #: and can be overwritten in client configuration. #: #: It's purpose is to provide a consistent way to wrap values, and allow #: simple extensibility. #: #: :func:`~siquant.quantities.make` is just a wrapper around this factory. #: #: .. code-block:: python #: #: def ext_factory(q, u): #: if isinstance(q, Vector): #: return VectorQuantity(q, u) #: return Quantity(q, u) #: #: SIUnit.factory = staticmethod(ext_factory) #: factory = None
[docs] @staticmethod def Unit(scale=1.0, kg=0, m=0, s=0, k=0, a=0, mol=0, cd=0): """Create a new SIUnit with a scale of provided base units. :param scale: The linear scaling factor of base units. :type scale: ``numbers.Real`` :param kg: The exponent of kilograms. :type kg: ``numbers.Real`` :param m: The exponent of meters. :type m: ``numbers.Real`` :param s: The exponent of seconds. :type s: ``numbers.Real`` :param k: The exponent of degrees Kelvin. :type k: ``numbers.Real`` :param a: The exponent of Amperes. :type a: ``numbers.Real`` :param mol: The exponent of mols. :type mol: ``numbers.Real`` :param cd: The exponent of candelas. :type cd: ``numbers.Real`` :rtype: :class:`SIUnit` """ return SIUnit(scale, SIDimensions(kg=kg, m=m, s=s, k=k, a=a, mol=mol, cd=cd))
def __init__(self, scale, dimensions): if scale <= 0: raise ValueError("SIunit scale must be positive.") super().__setattr__("scale", scale) super().__setattr__("dimensions", dimensions) kg = property(lambda self: self.dimensions[0]) m = property(lambda self: self.dimensions[1]) s = property(lambda self: self.dimensions[2]) k = property(lambda self: self.dimensions[3]) a = property(lambda self: self.dimensions[4]) mol = property(lambda self: self.dimensions[5]) cd = property(lambda self: self.dimensions[6])
[docs] def base_units(self): """Get an SIUnit of equivalent dimensions with a scaling factor of 1.0. :rtype: :class:`~siquant.units.SIUnit` """ return SIUnit(1.0, self.dimensions)
[docs] def compatible(self, units): """Check whether the provided units are of the same dimensions. :param units: The units to check. :type units: :class:`~siquant.units.SIUnit` :rtype: ``bool`` """ return self.dimensions == units.dimensions
[docs] def quantities(self, iterable): """Create quantities these units for all arguments. :param args (Iterable[Any]): The values to tag with units. :rtype: ``_Q`` = :class:`~siquant.quantities.Quantity` """ return (self.factory(value, self) for value in iterable)
def values(self, iterable): return (quantity.get_as(self) for quantity in iterable) def pack(self, *values): return self.quantities(values) def unpack(self, *quantities): return self.values(quantities) __call__ = quantities def __mul__(self, rhs): if isinstance(rhs, SIUnit): return SIUnit( self.scale * rhs.scale, dim_mul(self.dimensions, rhs.dimensions) ) return self.factory(rhs, self) def __rmul__(self, lhs): return self.factory(lhs, self) def __truediv__(self, rhs): if isinstance(rhs, SIUnit): return SIUnit( self.scale / rhs.scale, dim_div(self.dimensions, rhs.dimensions) ) return self.factory(1 / rhs, self) def __rtruediv__(self, lhs): return self.factory(lhs, ~self) def __pow__(self, rhs): try: return SIUnit(self.scale ** rhs, dim_pow(self.dimensions, rhs)) except TypeError: return NotImplemented def __invert__(self): return SIUnit(1 / self.scale, dim_div(SIDimensions(), self.dimensions)) def __eq__(self, other): if isinstance(other, SIUnit): return self.scale == other.scale and self.dimensions == other.dimensions return NotImplemented def __lt__(self, other): if isinstance(other, SIUnit): if self.dimensions != other.dimensions: raise UnitMismatchError(self, other) return self.scale < other.scale return NotImplemented def __ne__(self, other): if isinstance(other, SIUnit): return self.scale != other.scale or self.dimensions != other.dimensions return NotImplemented def __hash__(self): return hash((self.scale, self.dimensions)) def __str__(self): return "%g*%s" % (self.scale, dim_str(self.dimensions)) def __repr__(self): return "SIUnit(%f, %r)" % (self.scale, self.dimensions)