近日,在阅读《Fluent Python》的第2.9.2节时,有一个关于内存视图的例子,当时看的一知半解,后来查了一些资料,现在总结一下,以备后续查询;
示例复述
添加了一些额外的代码,便于更好理解
memoryview
>>> import array
>>> numbers = array.array('h', [-2, -1, 0, 1, 2])
>>> memv = memoryview(numbers)
>>> len(memv)
5
>>> memv[0]
-2
>>> memv_oct = memv.cast('B')
>>> memv_oct
<memory at 0x10869d7c8>
>>> memv_oct.tolist()
[254, 255, 255, 255, 0, 0, 1, 0, 2, 0]
>>> memv.tolist()
[-2, -1, 0, 1, 2]
>>> memv_oct[5] = 4
>>> numbers
array('h', [-2, -1, 1024, 1, 2])
>>> memv.tolist()
[-2, -1, 1024, 1, 2]
>>> memv_oct.tolist()
[254, 255, 255, 255, 0, 4, 1, 0, 2, 0]
我的理解和疑惑
nembers
是一个signed short int
类型的数组;memv
是使用上述数组创建的一个memoryview,即内存视图,它使memv
能够共享nembers
数组的内存,但不需要复制里面的内容,这使得memv
也能够访问和操作numbers
数组的元素;memv[0] # -2
也就可以理解了。memv.cast('B')
把memv
转换成一个unsigned char int
的新memoryview
,并返回给memv_oct
。memv_oct.tolist()
的元素比原始数组多了一倍:- signed short int在内存中是以2个字节存储,而
unsigned char int
在内存中则是1个字节存储。 memv.tolist()
和numbers
的内容还是一样的,所以memoryview
只是换个角度看同一个事物,即所谓的横看成岭侧成峰,远近高低各不同- 至此,我有一个不理解的地方:为什么
signed short int
类型的-2
转换成unsigned char int
类型时,变成254 255
?
- signed short int在内存中是以2个字节存储,而
memv_oct[5] = 4
将signed short int
类型的0
的高字节赋值成4
;在二进制的层面下看,即0000 0000 0000 0000
转换成了0000 0000 0010 0000
,也就是$2^{10}$,即1024;- 同时,我们也可以看到
memv.tolist()
也随着numbers
改变了。更能说明memoryview
只是对同一块数据的进行不同形式的表达;
解惑
正整数的情况可以理解,负整数的情况就理解不了,这说明正负整数在内存中的存储形式是不一样的。有了方向我们就直接搜一下吧。
signed short int
类型的原码最高位表示正负,0
代表正数,1
代表负数。- 它们内存中是以补码的形式存储的,其中正数的补码和原码相同;负数的补码,是其原码除符号位(即最高位)外,其余全部取反,再加1;
signed short int
类型的-2
,其原码为0100 0000 0000 0001
,除符号位取反,为1011 1111 1111 1111
,再加1,为0111 1111 1111 1111
。当以unsigned char int
类型读出来的时候,就成了254 255
了;-1
亦是同理,即255 255
;
总结
其实这都是《计算机组成原理》的基本知识,只是当我们习惯了使用一些高级编程语言的时候,对于这些底层的东西就不那么敏感了。