My goal is to give numpy.ndarray a different representation, since I want to represent some arrays with units. Thus, I programmed a class that inherits its attributes/ methods from numpy.ndarray. For the another representation I wanted to use the __repr__
magic method like:
class Quantitiy(np.ndarray):
def __new__(cls, value, unit=None, dtype=None, copy=True, order=None, subok=False, ndmin=0):
value = np.asarray(value)
obj = np.array(value, dtype=dtype, copy=copy, order=order,
subok=True, ndmin=ndmin).view(cls)
obj.__unit = util.def_unit(unit)
obj.__value = value
return obj
def __repr__(self):
prefix = '<{0} '.format(self.__class__.__name__)
sep = ','
arrstr = np.array2string(self.view(np.ndarray),
separator=sep,
prefix=prefix)
return '{0}{1} {2}>'.format(prefix, arrstr, self.__unit)
So far this works fine. However, if I want to access the inherited methods from numpy.ndarray I get a AttributeError
because __repr__
cant resolve self.__unit
.
I tried to solve this problem with a private method that defines the variable self.__unit
and called it within the __new__
method but without success:
class Quantitiy(np.ndarray):
def __new__(cls, value, unit=None, dtype=None, copy=True, order=None, subok=False, ndmin=0):
value = np.asarray(value)
obj = np.array(value, dtype=dtype, copy=copy, order=order, subok=True, ndmin=ndmin).view(cls)
# Here I call the private method to initialize self.__unit.
obj.__set_unit()
obj.__value = value
return obj
def __repr__(self):
prefix = '<{0} '.format(self.__class__.__name__)
sep = ','
arrstr = np.array2string(self.view(np.ndarray), separator=sep, prefix=prefix)
return '{0}{1} {2}>'.format(prefix, arrstr, self.__unit)
# New defined private class.
def __set_unit(self, unit):
self.__unit = util.def_unit(unit)
I can not solve this with something like cls.__unit = util.def_unit(unit)
in the __new__
method. I already tried to define a __init__
method after __new__
. Moreover, I tried to interchange the private methods with public methods.
What I expect:
>>> array = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
>>> q = Quantity(value, unit="meter / second")
>>> q
<Quantitiy [[1,2,3,4],
[5,6,7,8]] meter/second>
>>> q * q
>>> <Quantitiy [[ 1, 4, 9,16],
[25,36,49,64]] meter**2/second**2>
>>> q.min()
>>> <Quantitiy 1 meter/second>
The actual result is:
>>> array = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
>>> q = Quantity(value, unit="meter / second")
>>> q
<Quantitiy [[1,2,3,4],
[5,6,7,8]] meter/second>
>>> q * q
>>> <Quantitiy [[ 1, 4, 9,16],
[25,36,49,64]] meter**2/second**2>
# Up to here everything works fine.
>>> q.min()
>>> AttributeError: 'Quantitiy' object has no attribute
'_Quantitiy__unit'
Does anyone see the mistake and can help me?
Ok, the answer is - as usual - in the FineManual (and could be found searching for "subclassing numpy ndarray" - which is how I found it actually), and requires implementing __array_finalize__(self, obj)
:
import numpy as np
class Quantitiy(np.ndarray):
def __new__(cls, value, unit=None, dtype=None, copy=True, order=None, subok=False, ndmin=0):
value = np.asarray(value)
x = np.array(value, dtype=dtype, copy=copy, order=order, subok=True, ndmin=ndmin)
obj = x.view(type=cls)
obj._unit = unit
obj._value = value
return obj
def __repr__(self):
print("repr %s" % type(self))
prefix = '<{0} '.format(self.__class__.__name__)
sep = ','
arrstr = np.array2string(self.view(np.ndarray),
separator=sep,
prefix=prefix)
return '{0}{1} {2}>'.format(prefix, arrstr, self._unit)
def __array_finalize__(self, obj):
# see InfoArray.__array_finalize__ for comments
if obj is None:
return
self._unit = getattr(obj, '_unit', None)
self._value = getattr(obj, '_value', None)