Изменение подкласса "ndarray" на месте
Предположим, что у меня есть простой класс Test
, который наследует от numpy.ndarray
(документацию по ndarray
подклассу). Этот класс создает объект, который является частичным видом другого ndarray
. Теперь я хотел бы создать метод, который изменяет этот вид на месте. Что-то вроде:
import numpy
class Test(numpy.ndarray):
def __new__(cls, info='Test class'):
base = numpy.ndarray.__new__(cls, (6, 2), dtype=float)
view = base[0:2, :]
view.info = info
return view
def __array_finalize__(self, obj):
if obj is None:
return
self.info = getattr(obj, 'info', None)
def change_view(self, start, stop):
"""This is the method I'd like to implement"""
# Incorrect, but represents what I am looking for
self = self.base[start:stop]
if __name__ == '__main__':
t = Test()
print(t)
print(t.info)
t.change_view(1, 5)
print(t)
print(t.info)
Я бы ожидал, что метод .change_view(x)
изменит представление атрибута .base
, чтобы показать строки [1:5]
вместо [0:2]
, который по умолчанию используется в __new__()
.
Возможно ли это? Если да, то как? В противном случае, почему бы и нет? Обратите внимание, что .base
никогда не изменяется (он всегда занимает одно и то же пространство памяти), я просто хочу изменить его вид.
Примечания
- См. комментарии в
change_view()
. Я знаю, что self = self.base[start:stop]
неверен, и я знаю и понимаю, почему, но я думаю, что это короткий способ представить то, что я ищу.
- Как отмечено в ответах ниже, можно подумать о
self[:] = ...
или self.data = ...
. Однако это не сработает при изменении размера представления (что точно так).
Ссылки
Этот вопрос был уже задан здесь (или, по крайней мере, это аналогичный случай), но ответы касались решений, которые возвратили модифицированный массив (т.е. не на месте модификация) или который использовал объект-оболочку (т.е. имеющий атрибут с массивом, который должен быть изменен).
Я знаю эти возможности, и я знаю, что они могут быть проще или даже более удобными. Однако я хотел бы понять, почему (если это так) невозможно выполнить то, что я ищу.
Ответы
Ответ 1
1) Вы не можете изменить, какие данные экземпляр ndarray или экземпляр подкласса указывают на место. Извините! Оказывается, можно изменить, какие данные экземпляр ndarray указывает на место, но это ужасная идея и, надеюсь, скоро будет удалена:-). См. https://github.com/numpy/numpy/issues/7093
2) Ваша попытка присвоить self
как таковая изменит привязку переменной self
внутри локального связывания метода, и я боюсь, что это предполагает довольно фундаментальное непонимание того, как исполнение Python и объектная модель Работа. Я только привожу это, потому что успешно подклассифицировать ndarray
сложно. Действительно трудно. Свертывание невозможно. (Например, разработчики популярной библиотеки pandas
отказались.) Несмотря на вводящее в заблуждение впечатление от этой документации, numpy на самом деле не предназначен для поддержки подкласса, и он не работает очень хорошо. Я вообще рекомендую, чтобы никто вообще не пытался подкласса ndarray
; если вы еще не опытный программист на Python, чем это происходит в десять раз. Вероятно, мы бы удалили эту глупую страницу и отключили подклассификацию вообще, за исключением того, что она сломала бы обратную совместимость:-( Я предлагаю найти другую стратегию, чтобы подойти к вашей проблеме.
Ответ 2
Когда вы пишете код типа
var = value
он присваивает новое значение var
и отбрасывает старое значение, однако это верно только в текущей области, а все остальные псевдонимы до предыдущего значения var
остаются неизменными. Python self
ничего особенного, это просто переменная, поэтому, когда вы пишете
self = self.base[start:stop]
вы просто назначаете значение self.base[start:stop]
локальной переменной self
, но значение, которое было ранее указано в self
, не было изменено, а также все другие ссылки, значения (которые являются экземпляром вашего тестового класса, который вы хотите мутировать). Вы можете протестировать его со следующей реализацией change_view
:
def change_view(self, start, stop):
"""This is the method I'd like to implement"""
# Incorrect, but represents what I am looking for
print('before assignment')
print(self)
self = self.base[start:stop]
print('after assignment')
print(self)
print('end change_view')
Я думаю, что запуск вашего кода с помощью этой реализации должен немного разобраться.
Возможная реализация может быть схожа с этим:
def change_view(self, start, stop):
"""This is the method I'd like to implement"""
# Incorrect, but represents what I am looking for
self[:] = self.base[start:stop]
Важным отличием является то, что он изменяет значение, содержащееся в переменной self
, вместо назначения нового значения переменной. Я думаю, что что-то вроде этого должно работать с списками python, но numpy.ndarray
является более сложным. Более того, поскольку он реализован в C
, это может быть вообще невозможно. Например, вам нужно каким-то образом изменить размер представления, но вы не можете использовать numpy.ndarray.resize
, потому что представления не владеют его данными; просто присваивание size
и shape
тоже не будет работать.