魔术方法 __repr__ 使用 __new__ 方法导致 AttributeError

Magic method __repr__ leads to AttributeError with __new__ method

我的目标是给 numpy.ndarray 一个不同的表示,因为我想用单位表示一些数组。因此,我编写了一个 class,它从 numpy.ndarray 继承了它的属性/方法。对于另一种表示,我想使用 __repr__ 魔术方法,例如:

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)

到目前为止一切正常。但是,如果我想从 numpy.ndarray 访问继承的方法,我会得到一个 AttributeError 因为 __repr__ 无法解析 self.__unit.

我试图用定义变量 self.__unit 的私有方法解决这个问题,并在 __new__ 方法中调用它但没有成功:

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)

我无法用 __new__ 方法中的 cls.__unit = util.def_unit(unit) 之类的方法解决这个问题。我已经尝试在 __new__ 之后定义一个 __init__ 方法。此外,我尝试将私有方法与 public 方法互换。

我的期望:

>>> 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>

实际结果是:

>>> 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'

有没有人看到错误并可以帮助我?

好的,答案是 - 像往常一样 - in the FineManual (并且可以在搜索 "subclassing numpy ndarray" 时找到 - 这就是我实际找到它的方式),并且需要实施 __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)