bytes对象,不可变的字节序列

不少编程语言中的 字符串 都是由 字符数组 (或称为 字节序列 )来表示, C 语言就是这样。

char msg[] = "Hello, world!";

由于一个字节最多只能表示 256 种字符,用来表示英文字符绰绰有余,想覆盖非英文字符便捉襟见肘了。 为了表示众多的非英文字符(比如汉字),计算机先驱们发明了 多字节编码 ——通过多个字节来表示一个字符。 由于原始字节序列不维护编码信息,操作不慎便导致各种乱码现象。

Python 提供的解决方案是 Unicode 字符串 ( str )对象, Unicode 可以表示各种字符,无需关心编码。 然而存储或者网络通讯时,字符串对象不可避免要 序列化 成字节序列。 为此, Python 额外提供了字节序列对象 —— bytes 。

../../_images/97a891e5c93edaad298206ea9cdfcd4b.svg

如上图, str 对象统一表示一个 字符串 ,不需要关心编码; 计算机通过 字节序列 与存储介质和网络介质打交道,字节序列由 bytes 对象表示; 存储或传输 str 对象时,需要将其 序列化 成字节序列,序列化过程也是 编码 的过程。

好了,我们已经弄明白 str 对象以 bytes 之间的关系,这两者是 Python 中最重要的内建对象之一。 读者对 str 对象应该再熟悉不过了,但对更接近底层的 bytes 对象可能涉猎不多。 没关系,经过本节学习,你将彻底掌握它!

对象结构

bytes 对象用于表示由若干字节组成的 字节序列 以及相关的 操作 ,并不关心字节序列的 含义 。 因此, bytes 应该是一种 变长对象 ,内部由 C 数组实现。 Include/boolobject.h 头文件中的定义印证了我们的猜测:

typedef struct {
    PyObject_VAR_HEAD
    Py_hash_t ob_shash;
    char ob_sval[1];

    /* Invariants:
    *     ob_sval contains space for 'ob_size+1' elements.
    *     ob_sval[ob_size] == 0.
    *     ob_shash is the hash of the string or -1 if not computed yet.
    */
} PyBytesObject;
../../_images/75e914c558a5bb30223bf66ac54cfa63.svg

字节序列对象 PyBytesObject 中,确实藏着一个字符数组 ob_sval 。 注意到 ob_sval 数组长度定义为 1 ,这是 C 语言中定义 变长数组 的技巧。 这个技巧在前面章节( int 对象,永不溢出的整数 )中介绍过,这里不再赘述。 源码注释表明, Python 为待存储的字节序列额外分配一个字节,用于在末尾处保存 0 ,以便兼容 C 字符串。

此外,我们还留意到另一个字段 ob_shash ,它用于保存字节序列的 哈希值 。 Python 对象哈希值应用范围很广,比如 dict 字典对象依赖对象哈希值进行存储。 由于计算 bytes 对象哈希值需要遍历其内部的字符数组,开销相对较大。 因此, Python 选择将哈希值保存起来,以空间换时间,避免重复计算。

最后,以几个典型例子结束 bytes 对象结构介绍,以此加深理解:

../../_images/c806004309fcf32194df266eacd5eb23.svg

由此可见,就算空 bytes 对象( b’’ )也是要占用内存空间的,至少变长对象 公共头部 是少不了的。

>>> sys.getsizeof(b'')
33

bytes 对象占用的内存空间可分为以下个部分进行计算:

  • 变长对象公共头部 24 字节,ob_refcnt 、 ob_type 、 ob_size 每个字段各占用 8 字节;

  • 哈希值 ob_shash 占用 8 字节;

  • 字节序列本身,假设是 n 字节;

  • 额外 1 字节用于存储末尾处的 0 ;

因此, bytes 对象空间计算公式为 \(24+8+n+1\) ,即 \(33+n\),其中 \(n\) 为字节序列长度。

学习了 bytes 对象的结构,接下来我们还会探索 bytes 对象的行为以及 python 是如何利用 字节缓冲池 来优化单字节 bytes 对象(也可称为 字符对象 )的创建效率。 点击 更多章节,获取更多细节!

更多章节

洞悉 Python 虚拟机运行机制,探索高效程序设计之道!

到底如何才能提升我的 Python 开发水平,向更高一级的岗位迈进? 如果你有这些问题或者疑惑,请订阅我们的专栏,阅读更多章节:

https://cdn.fasionchan.com/python-source-course-qrcode.png

附录

订阅更新,获取更多学习资料,请关注我们的 微信公众号

微信搜索:小菜学编程

创作不易,如果觉得我们写得还行,就请我们喝杯咖啡吧😋

微信搜索:小菜学编程