子类化Numpy数组 - 传播属性

Ove*_*olt 14 python arrays attributes numpy subclass

我想知道numpy数组的自定义属性如何传播,即使数组通过类似的函数np.fromfunction.

例如,我的类ExampleTensor定义了一个attr默认设置为1 的属性.

import numpy as np

class ExampleTensor(np.ndarray):
    def __new__(cls, input_array):
        return np.asarray(input_array).view(cls)

    def __array_finalize__(self, obj) -> None:
        if obj is None: return
        # This attribute should be maintained!
        self.attr = getattr(obj, 'attr', 1)
Run Code Online (Sandbox Code Playgroud)

ExampleTensor实例之间的切片和基本操作将维护属性,但使用其他numpy函数则不会(可能因为它们创建了常规的numpy数组而不是ExampleTensors).我的问题:当从子类化的numpy数组实例构造常规numpy数组时,是否存在一个持久化自定义属性的解决方案?

重现问题的示例:

ex1 = ExampleTensor([[3, 4],[5, 6]])
ex1.attr = "some val"

print(ex1[0].attr)    # correctly outputs "some val"
print((ex1+ex1).attr) # correctly outputs "some val"

np.sum([ex1, ex1], axis=0).attr # Attribute Error: 'numpy.ndarray' object has no attribute 'attr'
Run Code Online (Sandbox Code Playgroud)

hyl*_*oop 6

import numpy as np

class ExampleTensor(np.ndarray):
    def __new__(cls, input_array):
        return np.asarray(input_array).view(cls)

    def __array_finalize__(self, obj) -> None:
        if obj is None: return
        # This attribute should be maintained!
        default_attributes = {"attr": 1}
        self.__dict__.update(default_attributes)  # another way to set attributes
Run Code Online (Sandbox Code Playgroud)

像这样实现array_ufunc方法

    def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):  # this method is called whenever you use a ufunc
        f = {
            "reduce": ufunc.reduce,
            "accumulate": ufunc.accumulate,
            "reduceat": ufunc.reduceat,
            "outer": ufunc.outer,
            "at": ufunc.at,
            "__call__": ufunc,
        }
        output = ExampleTensor(f[method](*(i.view(np.ndarray) for i in inputs), **kwargs))  # convert the inputs to np.ndarray to prevent recursion, call the function, then cast it back as ExampleTensor
        output.__dict__ = self.__dict__  # carry forward attributes
        return output
Run Code Online (Sandbox Code Playgroud)

测试

x = ExampleTensor(np.array([1,2,3]))
x.attr = 2

y0 = np.add(x, x)
print(y0, y0.attr)
y1 = np.add.outer(x, x)
print(y1, y1.attr)  # works even if called with method

[2 4 6] 2
[[2 3 4]
 [3 4 5]
 [4 5 6]] 2
Run Code Online (Sandbox Code Playgroud)

评论中的解释。

  • 另外,如果输入不是 ndarray(例如 float 或 int),则 `i.view(np.ndarray) for i in input` 会给出错误。使用“i.view(np.ndarray) if isinstance(i, ExampleTensor) else i for i in input”代替。 (2认同)