Магический метод __repr__ приводит к AttributeError с методом __new__ - PullRequest
0 голосов
/ 11 января 2019

Моя цель - дать numpy.ndarray другое представление, так как я хочу представить некоторые массивы с единицами измерения. Таким образом, я запрограммировал класс, который наследует его атрибуты / методы от 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)

Я не могу решить это с помощью чего-то вроде cls.__unit = util.def_unit(unit) в методе __new__. Я уже пытался определить метод __init__ после __new__. Более того, я попытался поменять приватные методы на публичные.

Что я ожидаю:

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

Кто-нибудь видит ошибку и может мне помочь?

1 Ответ

0 голосов
/ 11 января 2019

Хорошо, ответ - как обычно - в FineManual (и его можно найти в поиске «подкласса 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)
...