273 lines
8.0 KiB
Python
273 lines
8.0 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
pint.unit
|
|
~~~~~~~~~
|
|
|
|
Functions and classes related to unit definitions and conversions.
|
|
|
|
:copyright: 2016 by Pint Authors, see AUTHORS for more details.
|
|
:license: BSD, see LICENSE for more details.
|
|
"""
|
|
|
|
from __future__ import division, unicode_literals, print_function, absolute_import
|
|
|
|
import copy
|
|
import operator
|
|
from numbers import Number
|
|
|
|
from .util import UnitsContainer, SharedRegistryObject, fix_str_conversions
|
|
|
|
from .compat import string_types, NUMERIC_TYPES, long_type
|
|
from .formatting import siunitx_format_unit
|
|
from .definitions import UnitDefinition
|
|
|
|
|
|
@fix_str_conversions
|
|
class _Unit(SharedRegistryObject):
|
|
"""Implements a class to describe a unit supporting math operations.
|
|
|
|
:type units: UnitsContainer, str, Unit or Quantity.
|
|
|
|
"""
|
|
|
|
#: Default formatting string.
|
|
default_format = ''
|
|
|
|
def __reduce__(self):
|
|
from . import _build_unit
|
|
return _build_unit, (self._units, )
|
|
|
|
def __new__(cls, units):
|
|
inst = object.__new__(cls)
|
|
if isinstance(units, (UnitsContainer, UnitDefinition)):
|
|
inst._units = units
|
|
elif isinstance(units, string_types):
|
|
inst._units = inst._REGISTRY.parse_units(units)._units
|
|
elif isinstance(units, _Unit):
|
|
inst._units = units._units
|
|
else:
|
|
raise TypeError('units must be of type str, Unit or '
|
|
'UnitsContainer; not {0}.'.format(type(units)))
|
|
|
|
inst.__used = False
|
|
inst.__handling = None
|
|
return inst
|
|
|
|
@property
|
|
def debug_used(self):
|
|
return self.__used
|
|
|
|
def __copy__(self):
|
|
ret = self.__class__(self._units)
|
|
ret.__used = self.__used
|
|
return ret
|
|
|
|
def __deepcopy__(self, memo):
|
|
ret = self.__class__(copy.deepcopy(self._units))
|
|
ret.__used = self.__used
|
|
return ret
|
|
|
|
def __str__(self):
|
|
return format(self)
|
|
|
|
def __repr__(self):
|
|
return "<Unit('{0}')>".format(self._units)
|
|
|
|
def __format__(self, spec):
|
|
spec = spec or self.default_format
|
|
# special cases
|
|
if 'Lx' in spec: # the LaTeX siunitx code
|
|
opts = ''
|
|
ustr = siunitx_format_unit(self)
|
|
ret = r'\si[%s]{%s}'%( opts, ustr )
|
|
return ret
|
|
|
|
|
|
if '~' in spec:
|
|
if not self._units:
|
|
return ''
|
|
units = UnitsContainer(dict((self._REGISTRY._get_symbol(key),
|
|
value)
|
|
for key, value in self._units.items()))
|
|
spec = spec.replace('~', '')
|
|
else:
|
|
units = self._units
|
|
|
|
return '%s' % (format(units, spec))
|
|
|
|
def format_babel(self, spec='', **kwspec):
|
|
spec = spec or self.default_format
|
|
|
|
if '~' in spec:
|
|
if self.dimensionless:
|
|
return ''
|
|
units = UnitsContainer(dict((self._REGISTRY._get_symbol(key),
|
|
value)
|
|
for key, value in self._units.items()))
|
|
spec = spec.replace('~', '')
|
|
else:
|
|
units = self._units
|
|
|
|
return '%s' % (units.format_babel(spec, **kwspec))
|
|
|
|
# IPython related code
|
|
def _repr_html_(self):
|
|
return self.__format__('H')
|
|
|
|
def _repr_latex_(self):
|
|
return "$" + self.__format__('L') + "$"
|
|
|
|
@property
|
|
def dimensionless(self):
|
|
"""Return true if the Unit is dimensionless.
|
|
|
|
"""
|
|
return not bool(self.dimensionality)
|
|
|
|
@property
|
|
def dimensionality(self):
|
|
"""Unit's dimensionality (e.g. {length: 1, time: -1})
|
|
|
|
"""
|
|
try:
|
|
return self._dimensionality
|
|
except AttributeError:
|
|
dim = self._REGISTRY._get_dimensionality(self._units)
|
|
self._dimensionality = dim
|
|
|
|
return self._dimensionality
|
|
|
|
def compatible_units(self, *contexts):
|
|
if contexts:
|
|
with self._REGISTRY.context(*contexts):
|
|
return self._REGISTRY.get_compatible_units(self)
|
|
|
|
return self._REGISTRY.get_compatible_units(self)
|
|
|
|
def __mul__(self, other):
|
|
if self._check(other):
|
|
if isinstance(other, self.__class__):
|
|
return self.__class__(self._units*other._units)
|
|
else:
|
|
qself = self._REGISTRY.Quantity(1.0, self._units)
|
|
return qself * other
|
|
|
|
if isinstance(other, Number) and other == 1:
|
|
return self._REGISTRY.Quantity(other, self._units)
|
|
|
|
return self._REGISTRY.Quantity(1, self._units) * other
|
|
|
|
__rmul__ = __mul__
|
|
|
|
def __truediv__(self, other):
|
|
if self._check(other):
|
|
if isinstance(other, self.__class__):
|
|
return self.__class__(self._units/other._units)
|
|
else:
|
|
qself = 1.0 * self
|
|
return qself / other
|
|
|
|
return self._REGISTRY.Quantity(1/other, self._units)
|
|
|
|
def __rtruediv__(self, other):
|
|
# As Unit and Quantity both handle truediv with each other rtruediv can
|
|
# only be called for something different.
|
|
if isinstance(other, NUMERIC_TYPES):
|
|
return self._REGISTRY.Quantity(other, 1/self._units)
|
|
elif isinstance(other, UnitsContainer):
|
|
return self.__class__(other/self._units)
|
|
else:
|
|
return NotImplemented
|
|
|
|
__div__ = __truediv__
|
|
__rdiv__ = __rtruediv__
|
|
|
|
def __pow__(self, other):
|
|
if isinstance(other, NUMERIC_TYPES):
|
|
return self.__class__(self._units**other)
|
|
|
|
else:
|
|
mess = 'Cannot power Unit by {}'.format(type(other))
|
|
raise TypeError(mess)
|
|
|
|
def __hash__(self):
|
|
return self._units.__hash__()
|
|
|
|
def __eq__(self, other):
|
|
# We compare to the base class of Unit because each Unit class is
|
|
# unique.
|
|
if self._check(other):
|
|
if isinstance(other, self.__class__):
|
|
return self._units == other._units
|
|
else:
|
|
return other == self._REGISTRY.Quantity(1, self._units)
|
|
|
|
elif isinstance(other, NUMERIC_TYPES):
|
|
return other == self._REGISTRY.Quantity(1, self._units)
|
|
|
|
else:
|
|
return self._units == other
|
|
|
|
def __ne__(self, other):
|
|
return not (self == other)
|
|
|
|
def compare(self, other, op):
|
|
self_q = self._REGISTRY.Quantity(1, self)
|
|
|
|
if isinstance(other, NUMERIC_TYPES):
|
|
return self_q.compare(other, op)
|
|
elif isinstance(other, (_Unit, UnitsContainer, dict)):
|
|
return self_q.compare(self._REGISTRY.Quantity(1, other), op)
|
|
else:
|
|
return NotImplemented
|
|
|
|
__lt__ = lambda self, other: self.compare(other, op=operator.lt)
|
|
__le__ = lambda self, other: self.compare(other, op=operator.le)
|
|
__ge__ = lambda self, other: self.compare(other, op=operator.ge)
|
|
__gt__ = lambda self, other: self.compare(other, op=operator.gt)
|
|
|
|
def __int__(self):
|
|
return int(self._REGISTRY.Quantity(1, self._units))
|
|
|
|
def __long__(self):
|
|
return long_type(self._REGISTRY.Quantity(1, self._units))
|
|
|
|
def __float__(self):
|
|
return float(self._REGISTRY.Quantity(1, self._units))
|
|
|
|
def __complex__(self):
|
|
return complex(self._REGISTRY.Quantity(1, self._units))
|
|
|
|
__array_priority__ = 17
|
|
|
|
def __array_prepare__(self, array, context=None):
|
|
return 1
|
|
|
|
def __array_wrap__(self, array, context=None):
|
|
uf, objs, huh = context
|
|
|
|
if uf.__name__ in ('true_divide', 'divide', 'floor_divide'):
|
|
return self._REGISTRY.Quantity(array, 1/self._units)
|
|
elif uf.__name__ in ('multiply',):
|
|
return self._REGISTRY.Quantity(array, self._units)
|
|
else:
|
|
raise ValueError('Unsupproted operation for Unit')
|
|
|
|
@property
|
|
def systems(self):
|
|
out = set()
|
|
for uname in self._units.keys():
|
|
for sname, sys in self._REGISTRY._systems.items():
|
|
if uname in sys.members:
|
|
out.add(sname)
|
|
return frozenset(out)
|
|
|
|
|
|
def build_unit_class(registry):
|
|
|
|
class Unit(_Unit):
|
|
pass
|
|
|
|
Unit._REGISTRY = registry
|
|
return Unit
|