《Fluent|《Fluent Python》---一个关于memoryview例子的理解过程

近日,在阅读《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 >>> 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
  • memv_oct[5] = 4signed short int类型的0的高字节赋值成4;在二进制的层面下看,即0000 0000 0000 0000转换成了0000 0000 0010 0000,也就是,即1024;
  • 同时,我们也可以看到memv.tolist()也随着numbers改变了。更能说明memoryview只是对同一块数据的进行不同形式的表达;
解惑
【《Fluent|《Fluent Python》---一个关于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
总结 其实这都是《计算机组成原理》的基本知识,只是当我们习惯了使用一些高级编程语言的时候,对于这些底层的东西就不那么敏感了。

    推荐阅读