【问题标题】:Magic method __repr__ leads to AttributeError with __new__ method魔术方法 __repr__ 使用 __new__ 方法导致 AttributeError
【发布时间】:2019-06-06 07:54:06
【问题描述】:

我的目标是给 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)

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

我的期望:

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

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

【问题讨论】:

  • 谢谢@brunodesthuilliers。对不起,我忘了说我已经尝试在公共方法中更改私有方法,但没有成功。
  • 看来我第一次没有正确阅读您的代码。
  • 好吧,我怀疑 numpy 和你对 Quantitiy.__new__ 的巴洛克式(这是轻描淡写的)实现发生了一些奇怪的事情。 FWIW,使用“受保护”(一个前导下划线)甚至公共名称也会遇到同样的问题。请问您是从哪里得到实现__new__ 的想法的? (我的意思是:这是用 numpy 记录的收据还是什么?)
  • 我从stackoverflow 得到的。您对我如何实施它有更好的想法吗?我将不胜感激。是的,我在privat、protected 和public 名称中遇到同样的错误。但只有当我尝试访问 numpy 数组方法时才会出现错误。

标签: python-2.7 class methods superclass


【解决方案1】:

好的,答案是 - 像往常一样 - 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)

【讨论】:

  • 非常感谢!我一直在尝试__array_finalize__ 一段时间,因为我也看过手册。我只是不太明白该怎么做。非常感谢您的努力和时间。
猜你喜欢
  • 2015-08-08
  • 2016-04-09
  • 2015-09-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-14
  • 2010-10-27
相关资源
最近更新 更多