Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split Quantity into scalar and sequence classes #764

Closed
wants to merge 47 commits into from
Closed
Changes from 1 commit
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
3ad6e39
create quantityscalar and sequence classes
andrewgsavage Feb 12, 2019
0d0097d
add __array_function__
andrewgsavage Feb 14, 2019
cfc5d0d
Merge branch 'master' into split_quantity
andrewgsavage Feb 16, 2019
431d5b4
replace _Q with BaseQuantity
andrewgsavage Feb 16, 2019
f62e946
change np ufuncs to check x2 is a BaseQuantity
andrewgsavage Feb 17, 2019
bbb44d6
revert changes in test_quantity
andrewgsavage Feb 17, 2019
d76171f
revert some more
andrewgsavage Feb 17, 2019
30d542a
some more reverts
andrewgsavage Feb 17, 2019
5b9e473
revert more
andrewgsavage Feb 17, 2019
c21bfbd
revert getattr
andrewgsavage Feb 18, 2019
def8348
__used -> _used
andrewgsavage Feb 19, 2019
65905e0
fix path without _used
andrewgsavage Feb 19, 2019
7fb3800
fix setitem
andrewgsavage Feb 20, 2019
e07e237
fix put
andrewgsavage Feb 20, 2019
f6373fb
some array manip tests
andrewgsavage Feb 21, 2019
0aea9bc
remove broadcast
andrewgsavage Feb 21, 2019
077d12b
atleast nd
andrewgsavage Feb 21, 2019
da9dcb8
squuze
andrewgsavage Feb 21, 2019
2727d21
uprev numpy, disable less useful versions
andrewgsavage Feb 21, 2019
fef52dd
downrev numpy to one that may exist
andrewgsavage Feb 21, 2019
ba8cfe1
downrev numpy to one that may exist
andrewgsavage Feb 22, 2019
2f507f0
test dev numpy
andrewgsavage Feb 22, 2019
7c90ba7
test dev numpy
andrewgsavage Feb 22, 2019
f0b904e
pre release wheels
andrewgsavage Feb 22, 2019
4123d14
few more funcs
andrewgsavage Feb 23, 2019
ca0ecfd
try to get tests green
andrewgsavage Feb 24, 2019
3923c44
better way of skippping tests for other np versions
andrewgsavage Feb 24, 2019
1f4cd7f
set up np version for dev versions
andrewgsavage Feb 24, 2019
247700e
few more funcs
andrewgsavage Feb 24, 2019
284fe70
remove unexpected failure
andrewgsavage Feb 24, 2019
337e151
add more subclassed unexpected failure functions
andrewgsavage Feb 24, 2019
2f741a3
organise np tests
andrewgsavage Feb 25, 2019
d16f820
add Joining arrays tests/funcs
andrewgsavage Feb 25, 2019
0a84003
get unwrap workin
andrewgsavage Feb 25, 2019
346e0b4
rationalise conversion helper funcs
andrewgsavage Feb 25, 2019
67918ec
single implementation function
andrewgsavage Feb 25, 2019
afc7051
implement remaining subclass funcs
andrewgsavage Feb 26, 2019
e32e6e9
try testing np 1.16
andrewgsavage Mar 7, 2019
4dcbe78
.
andrewgsavage Mar 18, 2019
6c62a05
offset units
andrewgsavage Jul 28, 2019
a7f5841
Merge branch 'master' into split_quantity
andrewgsavage Jul 28, 2019
b57f012
add warning
andrewgsavage Aug 17, 2019
d3195d1
add warning
andrewgsavage Aug 17, 2019
d9ef27d
add warning
andrewgsavage Aug 17, 2019
ea6bebe
add warning
andrewgsavage Aug 17, 2019
aa66a07
add warning
andrewgsavage Aug 17, 2019
5a528ad
Merge pull request #5 from andrewgsavage/ags/split_quantity2
andrewgsavage Aug 17, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 78 additions & 53 deletions pint/quantity.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def wrapped(self, *args, **kwargs):


@fix_str_conversions
class _Quantity(PrettyIPython, SharedRegistryObject):
class BaseQuantity(PrettyIPython, SharedRegistryObject):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this classe "public" (i.e. no underscore)? Is this the class users should check against when using isinstance?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hadn't thought of this when going through it!

It should be used when the user does know the UnitRegistry. In the current version users should be checking with _Quantity (as done in pint-pandas)
Using isinstance with a Quantity from one UnitRegistry and another UnitRegistry yields false:
'''
ureg=pint.UnitRegistry()

ureg2 = pint.UnitRegistry()

isinstance(my_array,ureg2.Quantity)
False
'''
Should this be public?

"""Implements a class to describe a physical quantity:
the product of a numerical value and a unit of measurement.

Expand All @@ -95,7 +95,9 @@ def __reduce__(self):
from . import _build_quantity
return _build_quantity, (self.magnitude, self._units)

def __new__(cls, value, units=None):

@classmethod
def _new(cls, value, units=None):
if units is None:
if isinstance(value, string_types):
if value == '':
Expand Down Expand Up @@ -132,18 +134,8 @@ def __new__(cls, value, units=None):

inst.__used = False
inst.__handling = None
# Only instances where the magnitude is iterable should have __iter__()
if hasattr(inst._magnitude,"__iter__"):
inst.__iter__ = cls._iter
return inst

def _iter(self):
"""
Will be become __iter__() for instances with iterable magnitudes
"""
# # Allow exception to propagate in case of non-iterable magnitude
it_mag = iter(self.magnitude)
return iter((self.__class__(mag, self._units) for mag in it_mag))
@property
def debug_used(self):
return self.__used
Expand Down Expand Up @@ -825,7 +817,6 @@ def _imul_div(self, other, magnitude_op, units_op=None):

self._magnitude = magnitude_op(self._magnitude, other._magnitude)
self._units = units_op(self._units, other._units)

return self

@check_implemented
Expand Down Expand Up @@ -1395,42 +1386,6 @@ def __getattr__(self, item):
raise AttributeError("Neither Quantity object nor its magnitude ({}) "
"has attribute '{}'".format(self._magnitude, item))

def __getitem__(self, key):
try:
value = self._magnitude[key]
return self.__class__(value, self._units)
except TypeError:
raise TypeError("Neither Quantity object nor its magnitude ({})"
"supports indexing".format(self._magnitude))

def __setitem__(self, key, value):
try:
if math.isnan(value):
self._magnitude[key] = value
return
except (TypeError, DimensionalityError):
pass

try:
if isinstance(value, self.__class__):
factor = self.__class__(value.magnitude, value._units / self._units).to_root_units()
else:
factor = self.__class__(value, self._units ** (-1)).to_root_units()

if isinstance(factor, self.__class__):
if not factor.dimensionless:
raise DimensionalityError(value, self.units,
extra_msg='. Assign a quantity with the same dimensionality or '
'access the magnitude directly as '
'`obj.magnitude[%s] = %s`' % (key, value))
self._magnitude[key] = factor.magnitude
else:
self._magnitude[key] = factor

except TypeError:
raise TypeError("Neither Quantity object nor its magnitude ({})"
"supports indexing".format(self._magnitude))

def tolist(self):
units = self._units
return [self.__class__(value, units).tolist() if isinstance(value, list) else self.__class__(value, units)
Expand Down Expand Up @@ -1671,14 +1626,84 @@ def _ok_for_muldiv(self, no_offset_units=None):
def to_timedelta(self):
return datetime.timedelta(microseconds=self.to('microseconds').magnitude)

class QuantitySequenceMixin(object):
def __getitem__(self, key):
try:
value = self._magnitude[key]
return self.__class__(value, self._units)
except TypeError:
raise TypeError("Neither Quantity object nor its magnitude ({})"
"supports indexing".format(self._magnitude))

def __setitem__(self, key, value):
try:
if math.isnan(value):
self._magnitude[key] = value
return
except (TypeError, DimensionalityError):
pass

try:
if isinstance(value, self.__class__):
factor = self.__class__(value.magnitude, value._units / self._units).to_root_units()
else:
factor = self.__class__(value, self._units ** (-1)).to_root_units()

def build_quantity_class(registry, force_ndarray=False):
if isinstance(factor, self.__class__):
if not factor.dimensionless:
raise DimensionalityError(value, self.units,
extra_msg='. Assign a quantity with the same dimensionality or '
'access the magnitude directly as '
'`obj.magnitude[%s] = %s`' % (key, value))
self._magnitude[key] = factor.magnitude
else:
self._magnitude[key] = factor

except TypeError:
raise TypeError("Neither Quantity object nor its magnitude ({})"
"supports indexing".format(self._magnitude))
def __iter__(self):
"""
Will be become __iter__() for instances with iterable magnitudes
"""
# # Allow exception to propagate in case of non-iterable magnitude
it_mag = iter(self.magnitude)
return iter((self.__class__(mag, self._units) for mag in it_mag))

class Quantity(_Quantity):
pass

def build_quantity_class(registry, force_ndarray=False):

class Quantity(BaseQuantity):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or this is class to be used when calling isinstance

def __new__(cls, value, units=None):
if units is None:
if isinstance(value, string_types):
if value == '':
raise ValueError('Expression to parse as Quantity cannot '
'be an empty string.')
inst = cls._REGISTRY.parse_expression(value)
return cls.__new__(cls, inst)
elif isinstance(value, cls):
inst = copy.copy(value)
else:
inst = object.__new__(cls)
inst._magnitude = _to_magnitude(value, inst.force_ndarray)
inst._units = UnitsContainer()
return inst
if hasattr(value, "__iter__"):
return QuantitySequence._new(value,units)
else:
return QuantityScalar._new(value,units)

Quantity._REGISTRY = registry
Quantity.force_ndarray = force_ndarray


class QuantityScalar(Quantity):
def __new__(cls, value, units=None):
inst = Quantity.__new__(Quantity, value, units)
return inst

class QuantitySequence(Quantity,QuantitySequenceMixin):
def __new__(cls, value, units=None):
inst = Quantity.__new__(Quantity, value, units)
return inst
return Quantity