Каковы атрибуты ndarray: shape, dtype, ndim, size и strides?
shape — размеры осей, dtype — тип элементов, ndim — число осей, size — общее число элементов, strides — байтовые шаги между элементами по каждой оси (основа zero-copy срезов и транспонирования).
Атрибуты ndarray: shape, dtype, ndim, size, strides
Каждый массив NumPy хранит метаданные о своей структуре через набор атрибутов. Понимание этих атрибутов необходимо для отладки форм тензоров, работы с памятью и написания производительного кода.
shape
Кортеж целых чисел, описывающий размер каждой оси. Для вектора из 6 элементов shape равен (6,). Для матрицы 3x4 — (3, 4). Для трёхмерного тензора — (2, 3, 4).
dtype
Тип данных каждого элемента. NumPy поддерживает float32, float64, int8, int16, int32, int64, complex128, bool, object и другие. dtype влияет на точность вычислений и потребление памяти.
ndim
Количество осей (измерений). Равно len(arr.shape). Вектор имеет ndim=1, матрица — ndim=2, тензор изображений — ndim=3 или 4.
size
Общее количество элементов. Равно произведению всех чисел в shape. Для массива (2, 3, 4) size=24.
strides
Кортеж, задающий количество байт, на которое нужно сдвинуться в памяти, чтобы перейти к следующему элементу по каждой оси. Это ключ к пониманию того, как NumPy реализует срезы без копирования данных.
import numpy as np
arr = np.array([[1, 2, 3],
[4, 5, 6]], dtype=np.float64)
print(arr.shape) # (2, 3)
print(arr.dtype) # float64
print(arr.ndim) # 2
print(arr.size) # 6
print(arr.strides) # (24, 8) -- 3 элемента * 8 байт по строке, 8 байт по столбцу
# Проверим strides вручную:
# float64 занимает 8 байт
# чтобы перейти к следующей строке (ось 0), нужно пропустить 3 * 8 = 24 байта
# чтобы перейти к следующей колонке (ось 1), нужно пропустить 1 * 8 = 8 байт
# Транспонирование меняет strides, но не копирует данные:
t = arr.T
print(t.shape) # (3, 2)
print(t.strides) # (8, 24) -- порядок осей поменялся
# Срезы тоже не копируют:
slice_arr = arr[::2, ::2] # каждый второй элемент
print(slice_arr.strides) # shagи кратны исходным
# Проверка владения памятью:
print(t.base is arr) # True -- транспонирование -- это представление
print(arr.copy().base is None) # True -- копия независима
# Работа с 1D-массивом:
vec = np.arange(12, dtype=np.int32)
print(vec.shape) # (12,)
print(vec.strides) # (4,) -- int32 = 4 байта
# reshape без копирования:
mat = vec.reshape(3, 4)
print(mat.shape) # (3, 4)
print(mat.strides) # (16, 4) -- 4 * 4 = 16 по строке
print(mat.base is vec) # True
Как strides связаны с производительностью
При C-order (row-major, по умолчанию) последняя ось имеет минимальный stride. При Fortran-order (F-order) — первая. Итерация по строкам в C-order — cache-friendly. Итерация по столбцам — нет. Функции np.ascontiguousarray() и np.asfortranarray() приводят массив к нужному порядку, копируя при необходимости.
import numpy as np
# Проверка contiguous:
arr = np.ones((3, 4), order='C')
print(arr.flags['C_CONTIGUOUS']) # True
print(arr.flags['F_CONTIGUOUS']) # False
arr_f = np.ones((3, 4), order='F')
print(arr_f.flags['C_CONTIGUOUS']) # False
print(arr_f.flags['F_CONTIGUOUS']) # True
# as_strided для создания произвольных представлений:
from numpy.lib.stride_tricks import as_strided
base = np.arange(10, dtype=np.float32)
# Скользящее окно ширины 3:
window = as_strided(base, shape=(8, 3), strides=(4, 4))
print(window)
Подводные камни
- Транспонирование (
.T) и срезы возвращают представления, а не копии — изменение одного меняет другое. Всегда проверяйтеarr.baseилиarr.flags['OWNDATA']. as_stridedне проверяет выход за границы буфера. Неправильные strides приводят к чтению случайной памяти и трудноуловимым багам.- dtype по умолчанию зависит от платформы:
np.array([1, 2, 3])даёт int64 на Linux и int32 на Windows. Указывайте dtype явно в производственном коде. - size возвращает число элементов, а не байтовый размер. Байты:
arr.nbytes == arr.size * arr.itemsize. - reshape возможен без копии только если массив contiguous. Иначе NumPy делает копию молча — проверяйте через
np.shares_memory(). - F-contiguous массивы (Fortran-порядок) часто приходят из MATLAB или R-библиотек. Передача такого массива в C-only функцию даёт неожиданные результаты.
- shape одномерного массива —
(n,), а не(n, 1). Путаница между ними ломает broadcasting и матричные операции.
Common mistakes
- Объяснять
ndarray shape dtype stridesтолько синтаксисом без shape, dtype, состояния или режима выполнения. - Игнорировать leakage, воспроизводимость, пустые входы и скрытые копии данных.
- Не проверять production-симптомы: latency, память, ретраи, дрейф качества и несовпадение версий.
What the interviewer is testing
- Может ли связать
ndarray shape dtype stridesс реальным контрактом входов и выходов. - Упоминает ли тесты, метрики, reproducibility и диагностику ошибок.
- Видит ли различие между demo-кодом в ноутбуке и production-пайплайном.