引言

前两篇文章有介绍 Python 动态类型 共享引用,赋值和拷贝的原理,对 Python list 列表应该有一个深刻的了解。那么 NumPy 中的 ndarray 数组和 list 列表有啥区别呢。更多 Python 进阶系列文章,请参考 Python 进阶学习 玩转数据系列

内容提要:

  1. Python 中不同类型的内存需求
  2. NumPy ndarray Vs. Python list 内存需求
  3. NumPy ndarray Vs. Python list 结构
  4. NumPy ndarray Vs. Python list 计算
  5. NumPy subarray Vs. Python sublist
  6. NumPy ndarray Vs. Python list 总结

Python 中不同类型的内存需求

一个变量被赋值成不同类型的值,它在内存中的存储需求也是不一样的。如下:

from sys import getsizeof as byte_size

x = 5
print("byte size of int_type:{}".format(byte_size(x)))
x = 'hello'
print("byte size of str_type:{}".format(byte_size(x)))
x = True
print("byte size of bool_type:{}".format(byte_size(x)))
x = 5.0
print("byte size of float_type:{}".format(byte_size(x)))

输出:

byte size of int_type:28
byte size of str_type:54
byte size of bool_type:28
byte size of float_type:24

list 中每个元素所占用内存大小取决于每个元素的类型,如下:

from sys import getsizeof as byte_size

list_object = ['Five', 5, 5.0, True]
print("byte size of list_object:{}".format(byte_size(list_object)))
print("byte size of each list item in list_object:{}".format([byte_size(item) for item in list_object]))
print("total byte size of list items in list_object:{}".format(sum([byte_size(item) for item in list_object])))

输出:

byte size of list_object:96
byte size of each list item in list_object:[53, 28, 24, 28]
byte total size of list items in list_object:133

NumPy ndarray Vs. Python list 内存的需求

先通过代码来了解一下:

from sys import getsizeof as byte_size
import numpy as np

list_object = [1,2,3,4,5,6]
np_array = np.array(list_object, dtype='int16')

print("list_object:{}".format(list_object))
print("byte size of list_object:{}".format(byte_size(list_object)))
print("byte size of each list item in list_object:{}".format([byte_size(item) for item in list_object]))
print("total byte size of list items in list_object:{}\n".format(sum([byte_size(item) for item in list_object])))

print("np_array:{}".format(np_array))
print("byte size of each item in np_array:{}".format([item.nbytes for item in np_array]))
print("total byte size of items in np_array:{}".format(np_array.nbytes))

输出:
可见 NumPy array 数组所需内存更少

list_object:[1, 2, 3, 4, 5, 6]
byte size of list_object:112
byte size of each list item in list_object:[28, 28, 28, 28, 28, 28]
total byte size of list items in list_object:168

np_array:[1 2 3 4 5 6]
byte size of each item in np_array:[2, 2, 2, 2, 2, 2]
total byte size of items in np_array:12

NumPy ndarray Vs. Python list 结构:

NumPy ndarray:存储的是数组里元素的值

一个指向数据(内存或内存映射文件中的一块数据)的指针。
数据类型或 dtype,描述在数组中的固定大小值的格子。
一个表示数组形状(shape)的元组,表示各维度大小的元组。
一个跨度元组(stride),其中的整数指的是为了前进到当前维度下一个元素需要"跨过"的字节数。

python ndarray全部乘以一个数 python ndarray list_元组


Python list:存储的是 list 元素的引用,即内存地址,不是值

python ndarray全部乘以一个数 python ndarray list_numpy_02

NumPy ndarray Vs. Python list 计算:

NumPy ndarry 可以进行数据间的算术运算。
Python list 之间不可以直接进行算术运算

import numpy as np

list_object = [1,2,3,4,5,6]
np_array = np.array(list_object, dtype='int16')

list_object_plus= list_object + list_object
np_array_plus = np_array + np_array
np_array_minus = np_array - np_array 
np_array_divide = np_array / np_array
np_array_multi = np_array * np_array
print("list_object:{}".format(list_object))
print("list_object + list_object:{}\n".format(list_object_plus))

print("np_array:{}".format(np_array))
print("np_array + np_array:{}".format(np_array_plus))
print("np_array - np_array:{}".format(np_array_minus))
print("np_array * np_array:{}".format(np_array_divide))
print("np_array / np_array:{}".format(np_array_multi))

输出:

list_object:[1, 2, 3, 4, 5, 6]
list_object + list_object:[1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6]

np_array:[1 2 3 4 5 6]
np_array + np_array:[ 2  4  6  8 10 12]
np_array - np_array:[0 0 0 0 0 0]
np_array * np_array:[1. 1. 1. 1. 1. 1.]
np_array / np_array:[ 1  4  9 16 25 36]

NumPy subarray Vs. Python sublist

我们通过例子来看一下 subarray 和 sublist 的不同

import numpy as np

list_object = [1,2,3,4,5,6]
np_array = np.array(list_object, dtype='int16')

list_object_sub = list_object[0:4]
np_array_sub = np_array[0:4]

print("origin list_object:{}".format(list_object))
print("origin np_array:{}".format(np_array))
print("origin list_object_sub:{}".format(list_object_sub))
print("origin np_array_sub:{}".format(np_array_sub))

list_object_sub[0] = 0
np_array_sub[0] = 0


print("after changed list_object_sub:{}".format(list_object_sub))
print("after changed np_array_sub:{}".format(np_array_sub))
print("list_object:{}".format(list_object))
print("np_array:{}".format(np_array))

输出:
可以看出 NumPy subarray 就是一个原 array 的一个视图,它的改变会影响到原 array。而 Python sublist 其实是原 list 的一个浅copy,它的改动不会影响到原list。

origin list_object:[1, 2, 3, 4, 5, 6]
origin np_array:[1 2 3 4 5 6]
origin list_object_sub:[1, 2, 3, 4]
origin np_array_sub:[1 2 3 4]
after changed list_object_sub:[0, 2, 3, 4]
after changed np_array_sub:[0 2 3 4]
list_object:[1, 2, 3, 4, 5, 6]
np_array:[0 2 3 4 5 6]

NumPy ndarray Vs. Python list 总结:

NumPy Array

Python List

创建时size

创建 array时,size是固定的,一旦改变size,原来的array会被删除,重新创建一个新的array

list 的size可以动态变化

存储方式

连续内存空间

不连续内存空间

item 内存

每个item所占内存一样

每个item所占内存可以不一样

item 类型

每个item 类型必须一致

每个item类型可以不一样

总体内存

内存占用较少

内存占用比较多用来支持动太类型

矢量计算

直接支持大量数学计算

不直接支持