np.array的arr.itemsize与sys.getsizeof(arr[0])有何区别?

4

给定一个数组

arr = array([  9.93418544e+00,   1.17237323e+01,   1.34554537e+01,
         2.43598467e+01,   2.72818286e+01,   3.11868750e+01,...])

执行以下命令后,我会得到一些输出结果:
arr.itemsize # 8
type(arr[0]) # numpy.float64
sys.getsizeof(np.float64()) # 32
sys.getsizeof(arr[0]) # 32
arr.dtype # dtype('float64')

看起来itemsize不起作用。我想知道这是为什么?

我正在处理

print(sys.version)
3.5.5 | packaged by conda-forge | (default, Jul 24 2018, 01:52:17) [MSC v.1900 64 bit (AMD64)]
numpy==1.10.4

它确实存在Python对象和Numpy中元素所占大小之间的差异。请注意,numpy是用C++实现的,并不构建单独的“python对象”。 - Willem Van Onsem
1个回答

4

看起来 itemsize 没有正常工作。

实际上,这是由于 Python 对象与 numpy 中的项不同造成的。

在 Python 中,所有东西都是对象。数据被“装箱”。这意味着对于一个 int,我们得到:

>>> sys.getsizeof(2)
28

那是28个字节,很多。在大多数编程语言中,int占用2到8个字节不等。如果是32位的int,则需要4个字节。但是在Python中,一个对象有许多“上下文”。例如,一些字节被用来表示对象的类型等等。然而,Numpy并不是用Python实现的,它不是一个使用Python对象本身的库。它更像是用C实现的库,并且具有良好的Python接口。这意味着,一个列表[1, 4, 2, 5]不会以四个对int对象的引用构成的列表的形式存储在Python中,而是作为一个数组存储的,通常是带有“非装箱”的元素。因此,上述内容将占用4x32位和一些额外的空间来存储int。这样存储项可以更加高效地利用空间。这使得处理值变得更加容易,因为我们不是按照指针跟踪值,而是直接跟踪值(虽然在numpy数组中有一些存储引用的方法,但现在让我们忽略它)。此外,numpy数组的内存使用要比等价的Python列表(以及它持有的项)少得多。然而,如果从numpy数组中提取一个项目,就需要为其创建一个Python对象。因此,这意味着在这里将构造一个包含该值的numpy.float64对象,但其周围仍然有很多“上下文”,这会导致使用更多的内存。事实上,numpy构建特定类型对象的数组也具有一些影响。例如,如果使用numpy.int16,则意味着无法将大于32767的值存储在其中,因为该值不能用16位2补码表示。
>>> np.int16(32767)
32767
>>> np.int16(32768)
-32768

此外,如果不使用Python对象引用或其他“技巧”,就无法构造包含不同类型对象的数组。例如,Numpy会构造一个int16数组,这意味着它将160位解释为10个16位数字。在Python中,列表本身包含对对象的引用,Python对象知道自己的类型,因此我们可以将引用设置为另一种类型的对象。

1
在CPython中,它也有两个指针:一个指向堆上下一个对象,另一个指向堆上前一个对象。对于64位Python中的普通PyLongObject,成本是类型指针(8字节),ssize_t引用计数(8字节),ssize_t长度字段(8字节)和表示大小为1的32位(4字节)整数数组的数组(4字节),没有存储普通int的双向链接分配列表(对于参与循环垃圾收集的对象会有类似这样的东西,但不适用于不包含其他Python对象的不可变对象)。 - ShadowRanger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接