为何不同Numpy数组的data属性返回相同内存地址?
关于Numpy ndarray.data返回相同内存对象的疑问
问题场景
用户创建了以下Numpy数组并打印ndarray.data:
import numpy as np a = np.array([[1,2,3],[1,2,3]]) # 2d整数数组 b = np.array([[1,2,3],[1,2,5.0]]) # 2d浮点数数组 c = np.array([1,2,3,4,5,6,7,8,9]) # 1d整数数组 d = np.array([10,20,30]) # 另一个1d整数数组 # 打印指向数组数据起始位置的Python buffer对象 print(a.data) print(b.data) print(c.data) print(d.data)
根据文档描述,ndarray.data应返回指向数组数据起始位置的Python buffer对象,预期每个数组的内存地址不同,但实际输出为:
<memory at 0x000001EAE8B7CAD0> <memory at 0x000001EAE8B7CAD0> <memory at 0x000001EAE98E8A00> <memory at 0x000001EAE98E8A00>
前两个数组的buffer对象地址相同,后两个也相同,疑问:Numpy是根据数组维度推断内存buffer对象指针吗?生成相似地址的规则是什么?
解答
核心误区:混淆了两个地址
你看到的<memory at 0x...>是Python memoryview对象本身的内存地址,不是数组实际数据在内存中的地址。这是两个完全不同的概念:
- 数组实际数据的地址:是Numpy在堆上分配的、存储数组元素的内存块起始地址
- memoryview对象的地址:是Python为这个视图对象分配的小内存块地址,仅用于指向数组数据
为什么会出现相同地址?
这和Numpy无关,是Python的垃圾回收与内存复用机制导致的:
- 当你执行
print(a.data)时,Numpy会临时创建一个memoryview对象,打印完成后这个对象没有被任何变量引用,会被Python的垃圾回收器标记为可回收 - 当后续执行
print(b.data)时,Python的内存分配器会优先复用之前被回收的memoryview对象的内存空间,所以新创建的memoryview对象会使用和之前相同的地址
如果你把每个memoryview对象保存下来(避免被回收),就会看到所有地址都不同:
import numpy as np a = np.array([[1,2,3],[1,2,3]]) b = np.array([[1,2,3],[1,2,5.0]]) c = np.array([1,2,3,4,5,6,7,8,9]) d = np.array([10,20,30]) # 保存memoryview对象,防止被垃圾回收 mv_a = a.data mv_b = b.data mv_c = c.data mv_d = d.data print(mv_a) print(mv_b) print(mv_c) print(mv_d)
如何查看数组实际数据的内存地址?
要获取数组元素存储的真实内存地址,可以用以下两种方式:
- 使用
__array_interface__属性:
print(a.__array_interface__['data'][0]) # 输出数组a数据的起始地址 print(b.__array_interface__['data'][0]) # 输出数组b数据的起始地址
- 使用
ctypes.data属性:
print(a.ctypes.data) print(b.ctypes.data)
这两种方式得到的地址才是数组实际数据的内存位置,每个数组的这个地址都是不同的(除非数组是视图或者共享内存)。
内容的提问来源于stack exchange,提问作者darren
相关产品推荐
相关产品推荐

