为什么更改numpy子类的属性不会更改子类数组?

时间:2018-09-05 13:16:56

标签: python arrays numpy attributes subclass

子类化numpy数组时遇到以下问题

例如:

import numpy

class Example(numpy.ndarray):
   def __new__(cls, x, y):
       dt = [('x', 'float'), ('y', 'float')]
       buffer = numpy.array(zip(x, y),dtype=dt)
       obj = super(Example, cls).__new__(cls, buffer.shape, dtype=dt,
                                          buffer=buffer)
       obj.x = numpy.array(x, dtype='float')
       obj.y = numpy.array(y, dtype='float')
       return obj

   def __array_finalize__(self, obj):
        if obj is None: return
        self.x = getattr(obj, 'x', None)
        self.y = getattr(obj, 'y', None)

如果我使用obj.x和obj ['x']进行操作,则一个不会更改另一个。例如,这些操作将显示不同的结果

x = [1,2,3,4]
y = [1,1,1,1]

obj = Example(x,y)
obj.x = obj.x / 2.
print obj.x, obj['x']

obj = Example(x,y)
obj['x'] = obj['x'] / 2.
print obj.x, obj['x']

当我对其中一个(obj.x或obj ['x'])进行操作时,如何使另一个变化?

1 个答案:

答案 0 :(得分:0)

使用[('x', 'float'), ('y', 'float')]作为数据类型会告诉numpy创建一个structured array,其字段名为xy。 如您所示,可以使用方括号来访问它们。 现在,您还向类添加了属性(使用obj.<name>访问)。 但是,您已经为属性创建了新的数组。 要修复属性符号,您需要使xy属性通过这些名称而不是单独的数组指向数组字段。 所以改变

   obj.x = numpy.array(x, dtype='float')
   obj.y = numpy.array(y, dtype='float')

   obj.x = obj['x']
   obj.y = obj['y']

修改,此操作仅修复了问题中的第二个测试用例。分配给xy仍会分配一个新对象作为属性,而不是更新xy。 要解决此问题,有必要修改类的__setattr__方法(请参见here

def __setattr__(self, attr, value):
    if attr in ['x', 'y']:
         getattr(self, attr)[:] = attr
    else:
         setattr(self, attr, value)

但是,Numpy已经具有一个数组类型,可以访问作为属性的字段。 您可以这样使用它

obj = np.array(np.r_[x, y], dtype=[('x', 'float'), ('y', 'float')]) 
obj = obj.view(np.recarray)  

恭喜!您已经有效地重新实现了record arrays(嗯,np.recarray将不允许访问与属性ndarray具有属性或函数匹配的字段名称。因此,诸如mean或{{1 }}退出,而您的代码允许这些操作。 当您花费数小时来创建已有的numpy内容时,这总是一个好兆头(而且非常令人沮丧)。