问题描述
我对 numpy 中的所有权机制感到困惑。
import numpy as np
a = np.arange(10)
a.flags.owndata # True
id(a) # 140289740187168
前四行很明显,变量a
拥有id为140289740187168
的数据。
b = a
c = a.view()
d = a.reshape((2,5))
print(b.flags.owndata,b.base,id(b.base)) # True None 94817978163056
print(c.flags.owndata,c.base,id(c.base)) # False [0 1 2 3 4 5 6 7 8 9] 140289740187168
print(d.flags.owndata,d.base,id(d.base)) # False [0 1 2 3 4 5 6 7 8 9] 140289740187168
id(None) # 94817978163056
变量 c,d
都是 a
的“浅”拷贝,因此两者都没有自己的数据。 b
是 a
并拥有数据(与 a
共享)。
a = a.view()
print(id(a)) # 140289747003632
print(a.flags.owndata,a.base,id(a.base)) # False [0 1 2 3 4 5 6 7 8 9] 140289740187168
但是,将 a
的视图分配给 a
会创建一个 id 140289747003632
的新变量,并将数据所有权留给 id {{1} 的前一个旧 a
}.
问题是,由于旧的 140289740187168
已被新的 a
重载,将数据所有权转移到新的 a
会更合理。为什么旧的 a
仍然保留数据所有权?
解决方法
b = a
b
是 a
,只是同一个对象的不同名称。那甚至不是副本。
这些是views
。视图是一个新数组,但它使用相同的数据缓冲区(如 base
:
c = a.view()
d = a.reshape((2,5))
我喜欢用 __array_interface__
查看数组的基本属性:
In [210]: a = np.arange(10)
In [211]: a.__array_interface__
Out[211]:
{'data': (43515408,False),'strides': None,'descr': [('','<i8')],'typestr': '<i8','shape': (10,),'version': 3}
data[0]
是 a
的值或数据存储位置的某种表示。
view
将具有相同的“数据”(具有可能的偏移量)。否则 view
有自己的 strides
和 shape
。它是一个共享 base
:
In [212]: d = a.reshape((2,5))
In [213]: d.__array_interface__
Out[213]:
{'data': (43515408,'shape': (2,5),'version': 3}
将 view
分配给 a
不会更改原始数组或数据缓冲区。原始的 a
数组对象及其数据缓冲区仍然存在于内存中。
In [214]: a = a.view()
In [216]: a.__array_interface__['data']
Out[216]: (43515408,False)
如果 numpy
按照您的建议“更新”了 a.base
,它也必须针对原始 a
的所有视图(例如 d
)更新它。>
In [218]: id(a)
Out[218]: 139767778126704
In [219]: id(a.base)
Out[219]: 139768132465328
In [220]: id(d.base)
Out[220]: 139768132465328
虽然 python 和 numpy
维护某种引用计数来确定哪些对象是垃圾,但 numpy
不维护 views
的记录。也就是说,虽然 d.base
将 d
链接到 a
,但没有其他方式的链接。