数组元素的Numpy内存位置



所以最近我做了一个项目,作为优化,我想使用numpy数组,而不是内置的python列表。它将是一个在两个轴上都具有固定长度的2d阵列。我还想最大限度地使用现金,这样代码就可以尽可能快。然而,当使用id(var(函数时,我发现了意想不到的结果:

代码:

a = numpy.ascontiguousarray([1,2,3,4,5,6,7,8,9], dtype=numpy.int32)
for var in a:
print(hex(id(var)))

返回:

x1aaba10d8f0
0x1aaba1f33d0
0x1aaba10d8f0
0x1aaba1f33d0
0x1aaba10d8f0
0x1aaba1f33d0
0x1aaba10d8f0
0x1aaba1f33d0
0x1aaba10d8f0

对我来说,这是一个超级奇怪的cus,意味着两个变量位于同一个内存块中(这是一件事吗?(。不管怎样,是我没有正确理解吗?

作为一个附带问题,构建二维阵列的原始任务能否用成本较低的方法来完成?Numpy数组附带了许多我不需要的函数。我只需要两样东西:

  1. 能够用[::-1]语法反转它
  2. 检查其中一个是否有效

提前感谢所有的帮助:-(

id(var)并不像您想象的那样工作。实际上,id(var)为指定的对象var返回一个唯一的ID,但var不是a的单元格var是引用a单元格的Python对象。请注意,a不包含这样的对象,因为它的效率太低(并且数据不会像请求的那样连续(。您看到重复ID的原因是以前的var对象已被回收。

您真正想要的数组类型不清楚,目的也不清楚。但是,关于连续(或连续(和缓存的讨论表明,您不清楚Python是如何工作的。

首先,Python一直是面向对象的。整数、字符串、列表都是某个类的对象,具有关联的方法和属性。对于内置类,我们对存储没有什么发言权。

让我们列一个小清单:

In [89]: alist = [1,2,3,1000,1001,1000,'foobar']
In [90]: alist
Out[90]: [1, 2, 3, 1000, 1001, 1000, 'foobar']

列表有一个数据缓冲区,用于存储对内存中其他对象的引用(如果愿意,可以使用指针(。id可能会给出一些位置的概念,它不应该被理解为c语言意义上的"指针"。

对于此列表:

In [91]: [id(i) for i in alist]
Out[91]: 
[9784896,
9784928,
9784960,
140300786887792,
140300786888080,
140300786887792,
140300786115632]

1,2,3的id值较小,因为Python在开始时初始化了小整数(最多256(。因此,所有用途都将具有唯一的id。

In [92]: id(2)
Out[92]: 9784928

在列表创建中,1000似乎是唯一的,但在该上下文之外并不是唯一的。

In [93]: id(1001)
Out[93]: 140300786888592

看起来字符串也被缓存了,但这只是解释器的选择,我们不应该指望它

In [94]: id('foobar')
Out[94]: 140300786115632

反向列表是一个新列表,有自己的指针数组。但参考文献是一样的:

In [95]: rlist = alist[::-1]
In [96]: rlist
Out[96]: ['foobar', 1000, 1001, 1000, 3, 2, 1]
In [97]: rlist[5],id(rlist[5])
Out[97]: (2, 9784928)

[::-1]这样的索引操作应该只取决于列表中的项目数。它不取决于值实际指向的位置。其他副本也是如此。即使追加到数组也是相对独立于时间的(它在数据缓冲区中保持增长空间(。实际上,处理列表中的对象可能取决于它们在内存中的存储位置,但我们对此没有什么发言权。

A";2d";list实际上是一个包含列表元素的列表;嵌套列表。子列表存储在内存的其他位置,就像字符串和数字一样。从这个意义上说,嵌套列表是不连续的。

那么数组呢?

In [101]: x = np.arange(12)
In [102]: x
Out[102]: array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])
In [104]: x.__array_interface__
Out[104]: 
{'data': (57148880, False),
'strides': None,             # default (8,)
'descr': [('', '<i8')],
'typestr': '<i8',
'shape': (12,),
'version': 3}
In [105]: x.nbytes     # 12*8 bytes
Out[105]: 96

x是一个ndarray对象,具有shapestridesdtype等属性。还有一个数据缓冲区。在这种情况下是96字节长的c阵列;57148880. We can't use that number, but I find it useful when comparing thisarray_interfacedict across arrays. A视图"尤其将具有相同或相关的值。

In [106]: x.reshape(3,4)
Out[106]: 
array([[ 0,  1,  2,  3],
[ 4,  5,  6,  7],
[ 8,  9, 10, 11]])
In [107]: x.reshape(3,4).__array_interface__['data']
Out[107]: (57148880, False)
In [108]: x.reshape(3,4)[1,:].__array_interface__['data']
Out[108]: (57148912, False)     # 32 bytes later

数组数据缓冲区具有实际值,而不是引用。这里,对于intdtype,每8个字节被解释为"int64"值。

您的id迭代实际上需要一个列表[x[i] for i in range(n)]。数组的一个元素必须是"0";未装箱";,并且是类型为np.int64的新对象。虽然不是数组,但它与1元素数组有很多共同的属性。

In [110]: x[4].__array_interface__
Out[110]: 
{'data': (57106480, False),
...
'shape': (),....}

data的值与x的值无关。

只要在现有阵列上使用numpy方法,速度就很好,通常是等效列表方法的10倍。但是,如果你从一个列表开始,那么制作一个数组需要时间。并且像列表一样处理数组是缓慢的。

x的反面呢?

In [111]: x[::-1].__array_interface__
Out[111]: 
{'data': (57148968, False),
'strides': (-8,),
'descr': [('', '<i8')],
'typestr': '<i8',
'shape': (12,),
'version': 3}

这是一个新的数组,但具有不同的strides(-8,(,并且data指向缓冲区的末尾880+96-8

最新更新