当前位置: 首页 > news >正文

NumPy:数组复制与视图

在使用 NumPy 进行数据处理时,数组对象不仅可以被读取或修改,还经常需要在不同变量或不同数组之间进行“复制”。例如:将一个数组赋值给另一个变量、通过切片获取数组的一部分、或显式创建新的数组副本。

需要注意的是,这些操作在语法上都表现为“复制”,但在内存层面的行为却可能完全不同。

NumPy 数组由数据缓冲区(data buffer)与数组结构信息(shape、strides、dtype 等)两部分构成。不同“复制方式”的本质差异,在于这两部分是否被共享。

从内存共享的角度来看,NumPy 中数组之间的关系可以分为三类:

(1)引用(reference)

(2)视图(view)

(3)副本(copy)

理解这三种关系,是掌握 NumPy 数组行为的重要基础。

一、数组引用(Reference)

在 Python 中,如果将一个数组赋值给另一个变量:

b = a

此时并不会创建新的数组对象,而只是创建了一个新的变量名指向同一个数组。

示例:

import numpy as np a = np.array([[1, 2, 3], [4, 5, 6]]) b = ab[0, 0] = 99 print(a)

输出:

[[99 2 3] [ 4 5 6]]

说明:

该行为本质上属于引用(别名)绑定,而非拷贝。

变量 a 与 b 指向的是同一个数组对象,因此修改任意一方都会影响另一方。

可以通过 id() 函数查看对象标识:

print(id(a))print(id(b))

两个变量对应的对象标识是相同的。

二、数组视图(View)

NumPy 提供了一种重要机制:视图(view)。

视图是一个新的数组对象,它与原数组共享底层数据缓冲区。即:

• 数据共享

• 数组结构独立(shape、strides 通常独立)

因此:

• 修改视图的数据,会影响原数组的数据

• 修改视图的结构,通常不影响到原数组

需要注意的是,虽然视图通常具有独立的结构信息,但在某些操作中(如直接修改属性),仍需谨慎处理结构一致性。

这种设计的目的在于:

• 提高内存利用率

• 避免不必要的数据复制

• 提升大规模数组计算效率

1、创建视图的常用方法

view()

创建当前数组的数据视图,新数组对象与原数组共享底层数据。

ndarray.view(dtype=None, type=None)

参数说明:

• dtype:可选,指定新的数据类型

• type:可选,指定返回数组的子类

返回值:

返回一个新的 ndarray 对象(共享数据)。

示例:

a = np.array([[1, 2, 3], [4, 5, 6]]) b = a.view()b[0, 0] = 100 print(a)

输出:

[[100 2 3] [ 4 5 6]]

说明:

虽然 a 与 b 是不同的数组对象,但它们共享同一块数据内存,因此修改其中一个数组会影响另一个数组。

2、返回视图的常见操作

(1)切片

在 NumPy 中,基本索引(切片)通常返回视图。

示例:

a = np.array([[1, 2, 3], [4, 5, 6]]) b = a[:, 1:3]b[0, 0] = 99 print(a)

输出:

[[ 1 99 3] [ 4 5 6]]

说明:

数组 b 是 a 的切片视图,因此修改 b 的数据会影响原数组。

(2)数组形状重构时

在不改变数据内容的情况下,使用 reshape() 重新解释数组的形状。多数情况下返回视图。

示例:

a = np.arange(6)b = a.reshape(2, 3) b[0, 0] = 100print(a)

输出:

[100 1 2 3 4 5]

说明:

reshape() 在大多数情况下返回视图,因此 b 与 a 共享数据内存。修改 b 会反映到原数组 a 上。

(3)展平数组时

使用 numpy.ravel() 将数组展平为一维数组时,优先返回视图。

示例:

a = np.array([[1, 2, 3], [4, 5, 6]]) b = np.ravel(a)b[0] = 100print(a)

输出:

[[100 2 3] [ 4 5 6]]

三、数组副本(Copy)

如果希望获得一个完全独立的数组,则需要创建数组副本。

1、显式创建数组副本

NumPy 提供了 ndarray.copy() 和 numpy.copy() 两种复制方法。

ndarray.copy()

创建数组的完整副本,复制底层数据,新数组与原数组的数据完全独立。

ndarray.copy(order='C')

参数说明:

• order:内存布局方式(默认为 'C')

返回值:

返回一个新的独立的 ndarray 数组对象。

示例:

a = np.array([[1, 2, 3], [4, 5, 6]]) b = a.copy()b[0, 0] = 99 print(a)

输出:

[[1 2 3] [4 5 6]]

说明:

数组 b 是 a 的数据副本,因此修改 b 不会影响 a。

numpy.copy()

返回输入数组的副本。

numpy.copy(a)

参数说明:

• a:输入数组

返回值:

返回一个新的独立的 ndarray 数组对象。

示例:

a = np.array([[1, 2, 3], [4, 5, 6]]) b = np.copy(a)b[0, 0] = 99 print(a)

输出:

[[1 2 3] [4 5 6]]

说明:

函数 numpy.copy() 与 ndarray.copy() 的行为基本一致。

2、返回副本的常见操作

(1)高级索引时

当使用整数数组或布尔数组进行高级索引时,返回副本,不共享底层数据。

示例:

a = np.array([[1, 2, 3], [4, 5, 6]]) b = a[:, [1, 2]]b[0, 0] = 99 print(a)

输出:

[[1 2 3] [4 5 6]]

说明:

虽然语法类似切片,但因使用整数数组索引,返回副本。

需要注意的是,是否返回视图,并不取决于语法形式,而取决于索引类型。只要使用整数数组或布尔数组,即属于高级索引,将返回副本。

(2)展平数组时

使用 ndarray.flatten() 将数组展平为一维数组时,始终复制数据。

示例:

a = np.array([[1, 2, 3], [4, 5, 6]]) b = a.flatten()b[0] = 100print(a)

输出:

[[1 2 3] [4 5 6]]

说明:

flatten() 会返回数组副本,因此修改 b 不会影响原数组 a。

四、浅拷贝与深拷贝

在 Python 中,对象复制通常分为浅拷贝(shallow copy)和深拷贝(deep copy)两种方式,通常通过 copy 模块实现:

copy.copy()copy.deepcopy()

但对于 NumPy 数组来说,情况略有不同。

由于 NumPy 数组的数据通常存储在连续的内存块 中,因此 ndarray.copy() 通常已经复制了数组数据,在大多数情况下,它的行为相当于 深拷贝。

例如:

import numpy as np a = np.array([1,2,3])b = a.copy()

此时两个数组的数据完全独立。

需要注意的是,如果数组的元素类型 dtype 为 object,则数组中存储的是 Python 对象引用。在这种情况下,ndarray.copy() 只会复制数组结构,而不会递归复制对象本身,因此仍可能出现浅拷贝行为。

五、判断是否共享内存

NumPy 提供了两个函数,用于判断数组之间是否共享内存。

numpy.shares_memory()

判断两个数组是否共享同一块内存。

numpy.shares_memory(a, b)

返回值:

返回布尔值。

说明:

numpy.may_share_memory() 与其基本一致,但基于保守判断,判断两个数组是否可能共享内存。

示例:

a = np.arange(6).reshape(2, 3)b = a[:, 1:] print(np.shares_memory(a, b))

输出:

True

说明:

数组 b 是 a 的切片视图,因此两者共享数据内存。

如果数组是通过 copy() 创建:

c = a.copy()np.shares_memory(a,c)

输出:

False

说明两个数组的数据不共享内存,彼此独立。

📘 小结

NumPy 中的“复制”涉及数据缓冲区与数组结构两个层面。变量赋值仅创建引用;切片与 view 通常生成共享数据的视图;而 copy() 等操作才会复制底层数据。理解这些机制,有助于避免隐式数据修改,并提升数组计算的效率。

“点赞有美意,赞赏是鼓励”

http://www.cnnetsun.cn/news/1486513.html

相关文章:

  • Youtu-VL-4B-Instruct应用案例:智能客服、教育答题、内容审核,多行业落地解析
  • Fireworks与Icofx3完美搭配:5分钟搞定专业级ICO图标制作(附快捷方式美化技巧)
  • 告别单调!用LeaguePrank打造你的英雄联盟专属秀场
  • 嵌入式开发必看:NFS根文件系统挂载失败的5个常见原因及解决方法
  • # 发散创新:基于Python与OpenCV的手势识别系统实战详解在智能交互日益普
  • Cadence Allegro 17.4新手必看:原理图工程创建与文件管理的5个高效习惯
  • Wan2.2-I2V-A14B企业级部署:支持JWT鉴权与API调用频控的生产环境方案
  • react19和vue3的优缺点 对比
  • Spring Boot导出Excel时遇到Stream is closed?这个隐藏的坑你可能没发现
  • ViGEmBus内核驱动技术指南:从虚拟控制器到定制开发实践
  • SDMatte透明PNG元数据规范:EXIF/IPTC嵌入、版权信息自动写入功能
  • 告别飞书文档迁移困境:feishu-doc-export的自动化解决方案
  • AI辅助开发实战:用Python高效完成毕业设计与开题报告的技术路径
  • Comsol模拟热流固盐四场耦合:探索冻融条件下盐迁移的奇妙之旅
  • Matlab科研绘图实战:瀑布图(Waterfall)的配色优化与多场景应用
  • 手把手教你用LM358P搭建二阶巴特沃斯低通滤波器(附Multisim仿真)
  • 5大维度解锁信息自由:开源内容访问工具全攻略
  • 告别配对烦恼:用Auracast蓝牙广播,让手机、耳机和电视实现一拖多音频共享
  • COMSOL水力压裂中的应力-渗流-损伤模型仿真分析
  • 数学空间的构建艺术:从集合到结构化对象的演变之路
  • OpenClaw安全防护指南:Qwen3-32B镜像下的权限管控实践
  • AI辅助编程新体验:使用IDE插件集成MiniCPM-o-4.5模型
  • Cyclone IV电源设计全解析:从去耦电容选型到PDN工具使用
  • NaViL-9B生产应用:物流面单识别+地址结构化+异常信息高亮标注
  • ArcGIS模型构建器实战:一键加载上百个SHP文件(含子文件夹)的保姆级教程
  • 避坑指南:二分类模型评估中置信区间的常见错误与正确用法
  • Jellyfin演员头像总是不全?试试这个TMM刮削+本地导入的终极方案
  • 开源Java绩效考核系统推荐
  • Python类型注解演进全景图(从Python 3.5到3.12:哪些特性必须升级,哪些已被废弃)
  • 乐高Studio与Solidworks联动指南:如何让你的3D设计变成可拼装的积木模型