Имеет ли назначение с расширенными индексами данных массива индексирования?
Я медленно пытаюсь понять разницу между view
и copy
в numpy, а также mutable vs. immutable types.
Если я обращаюсь к части массива с 'advanced indexing', он должен вернуть копию. Это выглядит так:
In [1]: import numpy as np
In [2]: a = np.zeros((3,3))
In [3]: b = np.array(np.identity(3), dtype=bool)
In [4]: c = a[b]
In [5]: c[:] = 9
In [6]: a
Out[6]:
array([[ 0., 0., 0.],
[ 0., 0., 0.],
[ 0., 0., 0.]])
Так как c
- это просто копия, он не передает данные, а изменение не мутирует a
. Однако это меня смущает:
In [7]: a[b] = 1
In [8]: a
Out[8]:
array([[ 1., 0., 0.],
[ 0., 1., 0.],
[ 0., 0., 1.]])
Итак, кажется, даже если я использую расширенную индексацию, назначение по-прежнему обрабатывает вещь слева как представление. Очевидно, что строка a
в строке 2 - это тот же объект/данные, что и a
в строке 6, поскольку мутация c
не влияет на нее.
Итак, мой вопрос: есть ли a
в строке 8 тот же объект/данные, что и раньше (не считая диагонали, конечно), или это копия? Другими словами, были ли данные a
скопированы в новый a
или были ли эти данные мутированы на месте?
Например, это похоже на:
x = [1,2,3]
x += [4]
или как:
y = (1,2,3)
y += (4,)
Я не знаю, как это проверить, потому что в любом случае a.flags.owndata
есть True
. Пожалуйста, не стесняйтесь разрабатывать или отвечать на другой вопрос, если я думаю об этом в замешательстве.
Ответы
Ответ 1
Когда вы выполняете c = a[b]
, a.__get_item__
вызывается с b
в качестве единственного аргумента, и все, что возвращается, присваивается c
.
Когда вы выполняете a[b] = c
, a.__setitem__
вызывается с b
и c
в качестве аргументов, и все, что возвращается, тихо отбрасывается.
Поэтому, несмотря на наличие синтаксиса a[b]
, оба выражения делают разные вещи. Вы можете подклассифицировать ndarray
, перегрузить эти две функции и заставить их вести себя по-другому. Как по умолчанию в numpy, первый возвращает копию (если b
- массив), но последний изменяет a
на месте.
Ответ 2
Да, это тот же объект. Вот как вы проверяете:
>>> a
array([[ 0., 0., 0.],
[ 0., 0., 0.],
[ 0., 0., 0.]])
>>> a2 = a
>>> a[b] = 1
>>> a2 is a
True
>>> a2
array([[ 1., 0., 0.],
[ 0., 1., 0.],
[ 0., 0., 1.]])
Назначение некоторому выражению в Python - это не то же самое, что просто чтение значения этого выражения. Когда вы делаете c = a[b]
, с a[b]
справа от знака равенства, он возвращает новый объект. Когда вы делаете a[b] = 1
, с a[b]
слева от знака равенства, он изменяет исходный объект.
На самом деле выражение типа a[b] = 1
не может изменить то, к чему привязано имя a
. Код, обрабатывающий obj[index] = value
, только узнает объект obj
, а не то, какое имя было использовано для ссылки на этот объект, поэтому он не может изменить то, на что ссылается это имя.
Ответ 3
Похоже, это распространенное недоразумение, цитата из официального документа: (https://scipy-cookbook.readthedocs.io/items/ViewsVsCopies.html)
Эмпирическое правило здесь может быть следующим: в контексте индексации lvalue (т.е. Индексы размещаются в левом значении присваивания), представление или копия массива не создаются (поскольку в этом нет необходимости). Однако с обычными значениями вышеупомянутые правила для создания представлений действительно применяются.
Другими словами, понятие view
или copy
относится только к ситуации извлечения значений из numpy
объекта.