Python 现代开发学习笔记

(六)视图、拷贝与共享数据

1. 名称、数组对象与底下的数据

第二章中,两个名称可能指向同一个列表。数组还多了一层:两个不同的 ndarray 对象,也可能按不同索引方式访问同一块数据。

# 项目文件:demos/views_copies.py
# 赋值共享同一个数组对象,切片建立共享数据的视图,copy 单独保存数据。
import numpy as np

a = np.array([[10, 20], [30, 40], [50, 60]], dtype=np.int64)
alias, view, copied = a, a[:, 0], a[:, 0].copy()
view[1] = 300
print("同对象", alias is a, view is a)
print("修改后", a.tolist(), view.tolist(), copied.tolist())
print("共享数据", np.shares_memory(a, view), np.shares_memory(a, copied))
print("步幅", a.strides, view.strides, a.T.strides)
b = np.arange(6).reshape(3, 2)
print("连续重排共享", np.shares_memory(b, b.reshape(-1)))
print("转置再展平共享", np.shares_memory(b, b.T.reshape(-1)))

alias 与 a 是同一个数组对象。view 是第一列的切片,与 a 是不同对象,却共享数据;把 view[1] 改成 300,a 的第二行第一列也变成 300。copied 保存独立数据,仍为 [10,30,50]。

视图复用数据,通过自己的 shape、步幅等信息解释访问位置;拷贝另存元素。需要修改局部而保留原数据时,在读取后明确调用 copy。np.shares_memory(a,view) 用于核对两者是否实际共享数据。视图与拷贝

名称alias指向原数组,列切片通过不同元数据访问同一数据,copy另存一份并在修改后保持原值

2. 用 strides 追踪一次索引

int64 的每项占 8 字节。连续三行两列的 strides 为 (16,8):沿行轴前进一步跨过 16 字节,沿列轴前进一步跨过 8 字节。第一列视图的 strides 为 (16,),所以依次读到原数据中的第 1、3、5 个数。

转置后的步幅为 (8,16),可以用不同的访问次序读同一份数据。reshape 在能共享时建立视图,重新组织的数据无法直接按所需次序访问时则会复制。示例中连续数组展平仍共享,转置后再按默认次序展平则不共享;通过实际 shares_memory 结果核对,不把 reshape 一律当作复制或共享。

连续数组沿行跨16字节沿列跨8字节,列切片与转置改变步幅而复用原来的六个位置

笔记正文与全部题解 · Ctrl / ⌘ K