Files
deb-python-pint/pint/unit.py
2016-08-07 22:49:09 -03:00

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