如何对numpy.ndarray的子类进行子类化

6

我正在努力对我的numpy.ndarray的子类进行子类化。我不太理解问题所在,希望有人能解释以下情况,并说明如何做到我想做的事情。

我想要实现的目标:

我有一个numpy.ndarry的子类,它表现出我想要的行为(代码中的A类)。我想要子类化A(代码中的B类),以便B包含附加信息(名称)和方法(装饰的.simple_data方法)。

案例1:

import numpy as np

class A(np.ndarray):

    def __new__(cls,data):
        obj = np.asarray(data).view(cls)
        return obj

    def __array_finalize(self,obj):
        if obj is None: return

class B(A):

    def __init__(self,data,name):
        super(B,self).__init__(data)
        self.name = name

    @property
    def simple_data(self):
        return [data[0,:],data[:,0]]

if __name__ == '__main__':
    data = np.arange(20).reshape((4,5))
    b = B(data,'B')
    print type(b)
    print b.simple_data

运行这段代码会得到如下输出:
Traceback (most recent call last):
  File "ndsubclass.py", line 24, in <module>
    b = B(data,'B')
TypeError: __new__() takes exactly 2 arguments (3 given)

我假设这与B的构造中的“name”变量有关,由于A是numpy.array的子类,因此在调用B的init方法之前会调用A的new方法。因此,为了修复这个问题,我认为B还需要一个new方法来适当地处理附加参数。
我的猜测是像这样的东西:
def __new__(cls,data,name):
    obj = A(data)
    obj.name = name
    return obj

应该做到这一点,但是我如何更改obj的类?

情况2:

import numpy as np

class A(np.ndarray):

    def __new__(cls,data):
        obj = np.asarray(data).view(cls)
        return obj

    def __array_finalize__(self,obj):
        if obj is None: return

class B(A):

    def __new__(cls,data):
        obj = A(data)
        obj.view(cls)
        return obj

    def __array_finalize__(self,obj):
        if obj is None: return

    @property
    def simple_data(self):
        return [self[0,:],self[:,0]]

if __name__ == '__main__':
    data = np.arange(20).reshape((4,5))
    b = B(data)
    print type(b)
    print b.simple_data()

运行后的输出如下:
<class '__main__.A'>
Traceback (most recent call last):
  File "ndsubclass.py", line 30, in <module>
    print b.simple_data()
AttributeError: 'A' object has no attribute 'simple_data'

这让我惊讶,因为我原本期望的是:

<class '__main__.B'>
[array([0, 1, 2, 3, 4]), array([ 0,  5, 10, 15])]

我猜测B.new()中view()的调用未能正确设置obj的类。为什么会这样呢?
我很困惑,如果有人能够解释一下,我将不胜感激。
1个回答

4

对于案例1,最简单的方法是:

class B(A):
    def __new__(cls,data,name):
        obj = A.__new__(cls, data)
        obj.name = name
        return obj

__new__实际上是一个静态方法,它将类作为第一个参数而不是类方法,因此您可以直接使用要创建实例的类调用它。

对于案例2view不能原地工作,您需要将结果分配给某些内容,最简单的方法是:

class B(A):
    def __new__(cls,data):
        obj = A(data)
        return obj.view(cls)

此外,你在AB中定义了相同的__array_finalize__(可能只是一个笔误)-- 你不需要这样做。

啊...所以我可以通过将该类传递给A.__new__来强制A.__new__将数组数据转换为正确的类。这是Pythonic的做法吗? - Ben Whale
1
@Ben 我会使用 super(B, cls).__new__ 而不是 A.__new__,但是这是相当标准的。你也可以更改 A 以处理可变数量的参数或类似的内容,但这可能超出了这个问题的范围。 - agf
太好了!那么我对上述两种情况的猜测是正确的吗? - Ben Whale
好的...明白了。非常感谢你的所有帮助!如果我可以给你的答案点赞,我会的。 - Ben Whale

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接