Python np.arr.itemsize与sys.getsizeof(arr[0])

Python np.arr.itemsize与sys.getsizeof(arr[0]),python,numpy,types,numpy-ndarray,Python,Numpy,Types,Numpy Ndarray,给定一个数组 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.getsizeo

给定一个数组

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
似乎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]
在Python中不是作为包含四个
int
对象引用的列表存储的,而是作为一个数组存储的,通常包含“unbox”元素。因此,上面的计算将需要,因为
int
s每个都需要32位,4*32位,并且数组周围的“上下文”需要一些额外的空间

因此,物品以更节省空间的方式存储。这使得处理值变得更容易,因为我们这里不使用指针,而是直接使用值(有一些方法可以将引用存储在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对象知道它是什么类型,这意味着我们可以设置对另一个对象的引用,即另一种类型的对象。

确实如此,Python对象与元素的大小(单位为Numpy)之间存在差异。注意,NUMPY是用C++实现的,并且不构造单独的“Pyton对象”。“在CPython中,它还有两个指针:一个是下一个指针,另一个指向堆上的前一个对象。”对于64位Python上的普通
PyLongObject
,成本是指向类型(8字节)的指针、引用计数为
ssize\u t
(8字节)、长度字段为
ssize\u t
(8字节),以及表示大小(4字节)的32位(4字节)整数数组(在本例中为大小1)。对于纯
int
s,没有双链接的分配列表(对于参与循环垃圾收集的对象,有类似的配置,但对于不包含其他Python对象的不可变对象,则没有)。