Почему изменения в атрибутах в пустом подклассе не изменяют массив подкласса? - PullRequest
0 голосов
/ 05 сентября 2018

У меня возникает следующая проблема при создании подкласса массива 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 голосов
/ 05 сентября 2018

Использование [('x', 'float'), ('y', 'float')] в качестве типа данных сообщает numpy для создания структурированного массива с полями с именами x и y. Доступ к ним осуществляется с помощью скобок, как вы показали. Теперь вы также добавили атрибуты (доступ к которым осуществляется с помощью obj.<name>) для вашего класса. Тем не менее, вы создали новые массивы для атрибутов. Чтобы исправить обозначение атрибута, необходимо, чтобы атрибуты x и y указывали на поля массива по этим именам, а не на отдельные массивы. Так что поменяй

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

до

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

edit это исправляет только второй контрольный пример в вопросе. Присвоение x или y по-прежнему назначает новый объект в качестве атрибута, а не обновление x или y. Чтобы это исправить, необходимо изменить метод класса __setattr__ (см. здесь )

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)  

Так что поздравляю! Вы фактически повторно внедрили массив записей (ну, np.recarray не позволит получить доступ к именам полей, соответствующих атрибутам или функциям ndarray. Значит, такие имена, как mean или ndim, отсутствуют, в то время как ваш код позволит это). Это всегда хороший (и очень расстраивающий) признак, когда вы часами тратите на создание чего-то, что уже есть у numpy.

...