NumPy: вычисления и устройство массивов

View и copy: предсказание влияния изменений среза на исходный массив

Содержание курса

Атрибут .base: иллюстрация владения памятью и его ограничения

Каждый ndarray хранит атрибут .base. Если массив владеет своими данными сам — .base равен None. Если массив смотрит в чужую память (то есть является view) — .base указывает на объект, которому эта память принадлежит.

В простом случае это выглядит понятно:

import numpy as np

a = np.array([10, 20, 30, 40, 50])
v = a[0:3]          # view от a

print(v.base is a)  # True — v смотрит в память a

c = a.copy()
print(c.base is None)  # True — c владеет своими данными

Для этих двух случаев .base читается легко и интуитивно. На этом его удобство заканчивается.

Проблема возникает в цепочках. Если вы берёте срез от среза, .base у конечного объекта будет указывать не на ближайшего «родителя», а на того, кто реально владеет памятью — то есть на исходный массив или даже на какой-то промежуточный объект, в зависимости от внутреннего устройства NumPy:

a = np.array([10, 20, 30, 40, 50])
v1 = a[1:5]     # view от a
v2 = v1[0:2]    # view от v1 — но чья память?

print(v2.base is v1)  # может быть False
print(v2.base is a)   # может быть True

Здесь v2.base is v1 может дать False, потому что NumPy «пробрасывает» .base к реальному владельцу данных, минуя промежуточные объекты. Если вы рассчитываете на v2.base is v1 и получаете False, это не значит, что v2 — копия. Просто .base работает не так, как вы ожидаете.

Ещё одна ловушка: .base отвечает на вопрос «кто владеет памятью», но не на вопрос «разделяют ли вот эти два конкретных объекта память». Если у вас есть x и y, и вы хотите знать, связаны ли они, проверка x.base is y или y.base is x не гарантирует правильного ответа во всех сценариях.

Для этого есть np.shares_memory(x, y) — он отвечает именно на этот вопрос, без допущений о структуре цепочки.

.base полезен как быстрая иллюстрация: увидеть, что view ссылается на чужую память, а копия — нет. Но принимать на его основе решения в коде не стоит. Если нужна надёжная проверка — np.shares_memory.