《流畅的Python》示例 2-21 memoryview

发布于:2022-12-26 ⋅ 阅读:(600) ⋅ 点赞:(0)

在看这个示例的时候对于如何通过修改字节来改变原来数组里的值,产生了一些混淆,在此记录。

在研究这个示例前需要了解下 array 创建数组所用的类型码参数,以下列出了所能支持的类型码及其存储尺寸:

表格来自 array --- 高效的数值数组 — Python 3.9.6 文档

接下来看示例代码,第一步创建数组:

numbers = array.array('h', [-2, -1, 0, 1, 2])

这一步创建的数组所用的类型码为 h 即有符号短整型(unsigned short),以两个字节表示,而一个字节存储的是八位,也就是说这一步数组所存储的五个数,在内存里是这样的(左侧为低位,右侧为高位):

# -2
0111 1111 1111 1111
# -1 
1111 1111 1111 1111
# 0
0000 0000 0000 0000
# 1
1000 0000 0000 0000
# 2
0100 0000 0000 0000

对于 unsigned short 类型,其最高位存储符号,1为负数,0为正数,为负数时,其值是反着算的,可以根据上面 -1 和 -2 的表示方式来很好理解,-1 作为最大的负数,以全 1 来表示。

当我们执行 memoryview 函数时,实际上直接拿到了这些数在内存里的存储内容,例如第三步:

memv_oct = memv.cast('B')

这一步将 memv 里的内容转换为 B 类型,即无符号字符,内存中的数据实际上没有变化,变化的是解读这些内容的方式。在 B 类型中,一个字节表示一个 int 类型的数,不带符号。

也就是说,原来由两个字节表示的 unsigned short 类型,将会被视作两个 unsigned char,也就是两个 python 中的 int 类型:

# -2 0111 1111 1111 1111 被拆分为 254 和 255
# 254
0111 1111
# 255
1111 1111

# -1 1111 1111 1111 1111 被拆分为两个 255
# 255
1111 1111
# 255
1111 1111

# 0 0000 0000 0000 0000 被拆分为两个 0
# 0
0000 0000
# 0
0000 0000

# 1 1000 0000 0000 0000 被拆分为 1 和 0
# 1
1000 0000
# 0
0000 0000

# 2 0100 0000 0000 0000 被拆分为 2 和 0
# 2
0100 0000
# 0
0000 0000

因此有了第四步的结果:

memv_oct.tolist()
# [254, 255, 255, 255, 0, 0, 1, 0, 2, 0]

5 个数变成了 10 个数,但此时内存中的内容并没有变化,只是理解这些内容的方式变了而已。

而在第五步操作时,对内存进行了操作,即对此时的 memv_oct 的第 6 个数(索引从 0 开始)赋值为 4:

memv_otc[5] = 4

即第 6 个数 0 变成了 4,在内存中则为:

# 0 -> 4
0000 0000 -> 0010 0000

也就是原本表示第 3 个 unsigned short 类型的两个字节中,高位字节变成了 0010 0000

那么当回到 unsigned short 类型时,原本第三个是 0(0000 0000 0000 0000)变成了 1024(0000 0000 0010 0000)

也就是第六步的结果:

numbers
# array('h', [-2, -1, 1024, 1, 2])

这个例子里面没有直接对 numbers 进行操作,而是直接对 numbers 在内存中存储的字节进行了精确操作。

 我的Github:Cheereus (FanToolMan) (github.com)

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到