给出一个数组
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
答案 0 :(得分:1)
项目大小似乎无法正常工作。
确实如此,结果不同是由于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
对象的引用的列表存储的,而是作为数组存储的,通常带有“未装箱”元素。因此,假设int
分别占用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对象知道其类型,因此这意味着我们可以将引用设置为另一种类型的另一个对象。