Переопределить .T (транспонировать) в подкласс numpy ndarray - PullRequest
1 голос
/ 27 марта 2020

У меня есть трехмерный набор данных, где 1-е измерение дает тип переменной, а 2-е и 3-е измерения - пространственные индексы. Я пытаюсь сделать эти данные более удобными для пользователя, создав подкласс ndarray, содержащий данные, но с атрибутами, которые имеют разумные имена, которые указывают на соответствующее измерение переменной. Одним из типов переменных является температура, которую я хотел бы представить с атрибутом .T. Я пытаюсь установить его так:

self.T = self[8,:,:]

Однако это конфликтует с базовым атрибутом numpy для транспонирования массива. Обычно переопределение атрибута класса тривиально, однако в этом случае я получаю исключение, когда пытаюсь переписать атрибут. Ниже приведен минимальный пример той же проблемы:

import numpy as np

class foo(np.ndarray):
    def __new__(cls, input_array):
        obj = np.asarray(input_array).view(cls)
        obj.T = 100.0
        return obj

foo([1,2,3,4])

приводит к:

Traceback (most recent call last):
  File "tmp.py", line 9, in <module>
    foo([1,2,3,4])
  File "tmp.py", line 6, in __new__
    obj.T = 100.0
AttributeError: attribute 'T' of 'numpy.ndarray' objects is not writable

Я пытался использовать setattr(obj, 'T', 100.0) для установки атрибута, но результат тот же .

Очевидно, я мог бы просто сдаться и назвать свой атрибут .temperature или что-то еще. Однако .T будет гораздо более красноречивым для последующих математических выражений, которые будут сделаны с этими объектами данных. Как я могу заставить python / numpy переопределить этот атрибут?

Ответы [ 3 ]

2 голосов
/ 27 марта 2020

Для подкласса np.matrix, как определено в np.matrixlib.defmatrix:

@property
def T(self):
    """
    Returns the transpose of the matrix.
    ....
    """
    return self.transpose()
1 голос
/ 27 марта 2020

T не является обычным атрибутом, который находится в __dict__ или __slots__. Фактически, вы можете увидеть это сразу, потому что результат T изменится, если вы измените форму или содержимое массива.

Поскольку ndarray - это класс, записанный в C, он имеет специальные дескрипторы для атрибутов динамического c, которые он выставляет. T является одним из этих динамических c атрибутов, определенных как структура PyGetSetDef. Вы не можете переопределить его простым присваиванием, потому что нечего присваивать, но вы можете создать дескриптор, который переопределяет его на уровне класса.

Как подсказывает @ hpaulj , простейшим решением может быть использование property для реализации протокола дескриптора для вас:

import numpy as np

class foo(np.ndarray):
    @property
    def T(self):
        return self[8, :, :]

Более сложными альтернативами будет создание собственного типа дескриптора или даже расширение класс в C и написать свою собственную PyGetSetDef структуру. Все зависит от того, чего вы пытаетесь достичь.

1 голос
/ 27 марта 2020

Следуя примеру Безумного Физика и Хпаули, решение моего минимального рабочего примера:

import numpy as np

class foo(np.ndarray):
    def __new__(cls, input_array):
        obj = np.asarray(input_array).view(cls)
        return obj

@property
def T():
    return 100.0

x = foo([1,2,3,4])
print("T is", x.T)

Что приводит к:

T is [1 2 3 4]
...