View и copy: предсказание влияния изменений среза на исходный массив
Содержание курса
Срезы в NumPy вы уже использовали — синтаксис start:stop:step знаком с урока по индексации. Но за этим синтаксисом скрывается поведение, которое легко упустить: срез не копирует данные, а возвращает другой «взгляд» на те же байты в памяти. Именно это поведение определяет, что произойдёт с исходным массивом, если вы изменили срез — и этому посвящён весь урок.
View и copy: два режима доступа к памяти ndarray
Когда вы пишете v = a[1:4], NumPy не выделяет новый кусок памяти под три элемента. Вместо этого v — это отдельный объект ndarray, который знает: «мои данные начинаются вот здесь в памяти, шаг такой-то, длина такая-то». Байты, в которых хранятся числа, общие — и у a, и у v. Такой объект называется view (представление).
Последствие прямое: если вы изменяете элемент через v, вы фактически обращаетесь к тем же байтам в памяти, на которые смотрит a. Никакой «защиты» нет — это не баг, а намеренное поведение NumPy ради экономии памяти и скорости.
import numpy as np
a = np.array([10, 20, 30, 40, 50])
v = a[1:4] # view: элементы 20, 30, 40
v[0] = 99 # меняем первый элемент view
print(a) # [10 99 30 40 50] — исходный тоже изменился
Это и есть view в действии: v[0] и a[1] — один и тот же элемент в общей области памяти.
Если вам нужна независимая копия — объект, который хранит те же значения, но в отдельном куске памяти — нужно явно попросить об этом:
a2 = np.array([10, 20, 30, 40, 50])
c = a2[1:4].copy() # независимая копия
c[0] = 99
print(a2) # [10 20 30 40 50] — исходный не тронут
Метод .copy() выделяет новое хранилище и переписывает туда значения. После этого c и a2 ничего не знают друг о друге на уровне памяти.
Разница между view и copy — не в значениях (при создании они одинаковы), а в том, где эти значения живут. View смотрит в чужую память, copy владеет своей. Это разграничение и определяет всё поведение, которое мы разберём дальше.
