# 流畅的 Python 示例 2-21

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

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

类型码 C 类型 Python 类型 以字节表示的最小尺寸 注释
'b' signed char int 1
'B' unsigned char int 1
'u' wchar_t Unicode 字符 2 (1)
'h' signed short int 2
'H' unsigned short int 2
'i' signed int int 2
'I' unsigned int int 2
'l' signed long int 4
'L' unsigned long int 4
'q' signed long long int 8
'Q' unsigned long long int 8
'f' float float 4
'd' double float 8

表格来自 array --- 高效的数值数组 — Python 3.9.6 文档 (opens new window)

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

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 在内存中存储的字节进行了精确操作。

Copyright © 2020 CheeReus_11 | 鄂ICP备2020022304号

鄂公网安备 42011502001280号