Изменили обучающий срез и испортили исходные данные: view и copy
Для эксперимента взяли первые строки массива и заменили выброс. После этого исходные данные тоже изменились. Обычный срез NumPy часто разделяет память с исходником, поэтому присваивание в срез меняет общие элементы.
Различать форму и владение памятью
Две переменные могут показывать разные формы и всё равно ссылаться на одну память. Базовая индексация и срезы обычно создают view. Расширенная индексация массивом индексов создаёт копию. На поведение нельзя переносить правило из списков Python.
Если функция должна лишь читать данные, разделяемый view удобен. Если она меняет значения, решите, разрешено ли изменение входа. Это часть интерфейса функции. Для независимого эксперимента создайте copy на границе, а не надейтесь, что конкретная индексация случайно скопировала массив.
Показать обе стороны изменения
В примере сначала меняем view и проверяем исходник. Затем создаём независимую копию другой части и меняем её, проверяя сохранность исходных значений. np.shares_memory подтверждает связь, но основная проверка — реальное значение после операции.
Исходные числа намеренно различаются. Это позволяет увидеть точную позицию изменения и не принять совпадение значений за отсутствие общей памяти.
import numpy as np
source = np.arange(12, dtype=float).reshape(4, 3)
view = source[:2]
assert np.shares_memory(source, view)
view[0, 0] = -99
assert source[0, 0] == -99
independent = source[2:].copy()
before = source.copy()
independent[0, 0] = -500
print({"source_row": source[2].tolist(), "copy_row": independent[0].tolist()})
assert not np.shares_memory(source, independent)
assert np.array_equal(source, before)
assert source[2, 0] == 6 and independent[0, 0] == -500Первое присваивание изменило исходник, второе — только копию. Проверка после изменения важна: одно сравнение object identity не показывает разделение памяти между массивами.
Выбрать границу копирования
Не обязательно копировать на каждом шаге. Если большой массив используется только для чтения, лишние копии увеличивают память. Удобнее определить одну границу: вход функции считается неизменяемым, а функция создаёт рабочую копию перед первой записью. Либо интерфейс явно разрешает изменение входа.
reshape способен вернуть view или потребовать копию в зависимости от расположения данных. Поэтому изменение формы не является гарантией независимости. Если независимость нужна по смыслу, вызывайте copy явно и проверяйте её на характерном входе.
У массивов dtype=object копирование массива не копирует рекурсивно объекты внутри. Изменение вложенного списка может оставаться общим. Здесь пример использует обычные числовые элементы; для объектных структур нужен отдельный договор копирования.
В обработке данных скрытая запись особенно опасна, когда один исходник используется несколькими экспериментами. Сохраните контрольный снимок маленького входа и проверьте, что функция, объявленная чистой, оставляет его неизменным. Это обнаружит зависимость результатов от порядка запусков в ноутбуке.
Закрепить на практике
В бесплатном уроке view и copy вы будете предсказывать, как изменение среза влияет на исходный массив. Пример статьи связывает эту практику с границей владения данными в функции. Откройте бесплатный урок и задачи. Полная последовательность тем — в бесплатном курсе.
Продолжить на практике
- NumPy и pandas: практический тренажёр ↗
Бесплатный тренажёр NumPy и pandas для тех, кто знает основы Python. Научитесь очищать таблицы, объединять данные и собирать отчёты: 31 урок и 124 задачи с кодом и автоматической проверкой.
