美文网首页我爱编程「Python3学习笔记」读书笔记
「Python3学习笔记」读书笔记—字节数组

「Python3学习笔记」读书笔记—字节数组

作者: Lovemma | 来源:发表于2018-06-21 23:10 被阅读0次

    生物都是由细胞构成的,但在我们普通人眼中,并不会将鸡、鸭、狗、鸟这些动物当作细胞看待,因为对待事物的角度决定了我们更关心生物的外在形状和行为,而不是它的组织构成。

    从计算机底层实现来说,所有的数据都是二进制字节序列。但为了更好地表达某个逻辑,计算机科学家们将数据抽象成不同的类型,犹如细胞和动物的关系。在编程语言中,对于字节序列,我们更关心的是它的存储和传输方式;而面向对象时,则着重于它的抽象属性。尽管两面一体,但从不混为一谈。

    同为不可变序列类型,bytes 与 str 有着非常相似的操作方式。其同样支持加法、乘法等运算符。

    >>> a = b"abc"
    >>> b = a + b"def"
    
    >>> b
    b'abcdef'
    
    >>> b.startswith(b"a")
    True
    >>> b.upper()
    b'ABCDEF'
    
    >>> b"abc" * 2
    b'abcabc'
    

    相比于 bytes 类型的一次性分配内存,bytearry 可按需扩张,更适合作为可读写缓冲区使用。如有必要,还可为其提前分配足够的内存,避免中途扩张造成的额外消耗。

    >>> b = bytearray(b"abc")
    >>> len(b)
    3
    >>> id(b)
    4473445824
    >>> b.append(ord("d"))
    >>> b.extend(b"e")
    >>> id(b)
    4473445824
    

    内存视图

    当我们要引用字节数据的某个片段的时候,需要考虑到:是否会有数据复制行为?是否能同步修改?

    >>> a = bytearray([0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16])
    >>> x = a[2:5]  # 引用片段
    >>> x
    bytearray(b'\x12\x13\x14')
    >>> a[3] = 0xEE # 修改原数据
    >>> a
    bytearray(b'\x10\x11\x12\xee\x14\x15\x16')
    >>> x       # 并未同步修改,可以看出仅仅只是数据复制
    bytearray(b'\x12\x13\x14')
    

    为什么需要引用某个片段,而不是整个对象呢?

    以自定义网络协议为例,通常由标准头和数据体两部分组成。如要验证数据是否被修改,总不能将整个包作为参数交给验证函数吧。因为如果将整个包传给函数,这势必要求该函数了解协议包的结构,这显然是不合理的设计,而复制数据体又可能导致重大性能开销,同样得不偿失。

    在 Python 中没有指针的概念,外加内存安全模型的限制,要做到这一点并不容易。此时,可以借助一种名为内存视图(Memory View)的方式来访问底层内存数据。

    内存视图要求目标对象支持缓冲协议(Buffer Protocol),内存视图直接引用目标内存,没有额外的复制行为,因此,可读取最新的数据,在目标对象允许的情况下,还可以执行写操作。

    >>> a = bytearray([0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16])
    >>> v = memoryview(a)       # 完整的视图
    >>> x = v[2:5]      # 视图片段
    >>> x.hex()
    '121314'
    
    >>> a[3] = 0xee     # 对原数据进行修改,可通过视图观察到
    >>> a
    bytearray(b'\x10\x11\x12\xee\x14\x15\x16')
    >>> x.hex()
    '12ee14'
    
    >>> x[1] = 0x13     # 因为引用了相同的内存区域,可通过视图修改原数据
    >>> a
    bytearray(b'\x10\x11\x12\x13\x14\x15\x16')
    

    当然,能够通过视图修改原数据,还必须得看原对象是否允许。

    >>> a = b"\x10\x11"     # bytes 是不可变类型
    >>> v = memoryview(a)
    >>> v[1] = 0xEE
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: cannot modify read-only memory
    

    如果要复制视图数据,可调用 tobytes、tolist 方法,复制后的数据与原对象无关,同样不会影响视图本身。

    >>> a = bytearray([0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16])
    >>> v = memoryview(a)
    >>> x = v[2:5]
    >>> b = x.tobytes()     # 复制并返回视图数据
    >>> b
    b'\x12\x13\x14'
    >>> a[3] = 0xEE     # 对原数据进行修改
    >>> a
    bytearray(b'\x10\x11\x12\xee\x14\x15\x16')
    >>> b       # 不影响复制数据
    b'\x12\x13\x14'
    

    除了上述这些,内存视图还为我们提供了一种内存管理手段,比如:通过 bytearray 预申请很大的一块内存,随后以视图方式将不同片段交给不同的逻辑使用。因为逻辑不能跨界访问,故此可以实现简易的内存分配器模式。对于 Python 这种限制较多的语言,合理使用视图可在不同使用 ctypes 等复制扩展的前提下,改善算法类型。

    可使用 memoryview.cast、struct.unpack 将字节数组转换为目标类型。

    从博客搬运到简书:原文链接

    相关文章

      网友评论

        本文标题:「Python3学习笔记」读书笔记—字节数组

        本文链接:https://www.haomeiwen.com/subject/gakhyftx.html