我们上一篇学习了两个字节序列对象进行相加“+”,concat这个函数的底层实现原理。我们看一下当有三个字节序列对象是如何相加的。因为字节对象是不可变对象,不可变对象在进行运算时会再创建一个新的不可变对象。例如有是三个bytes对象进行相加,bytes_result = a+b+c,执行步骤是这样的,临时对象t = a+b, 然后bytes_result = t + c。在这个过程中,a,b的数据被拷贝了两遍。
bytes_result = b''
for bytes_str in bytes_list:
bytes_result += bytes_str
写出上边这段代码,我们计算一下,假设上边这段代码合并了n个bytes对象,头两个对象需要拷贝n-1次,只有最后一个对象不需要重复拷贝。
这是一种效率非常低下的做法,运算过程中涉及了大量临时对象的调度的创建和销毁,后面我们学习的字符串也是这个道理。官方文档推介使用内建join方法,字节序列和字符串都可以对一个列表进行join,将列表里边的多个字节序列或者字符串join在一起。
bytes_result = b''.join(bytes_str for bytes_str in bytes_list)
join对数据拷贝进行了优化,先遍历待合并对象,计算总长度;然后根据总长度创建目标对象,逐一拷贝对象。通过这个方法,每个对象只需要拷贝一次,不需要重复拷贝。
/*Concatenate any number of bytes objects.
//连接任意数量的字节对象
The bytes whose method is called is inserted in between each pair.
//调用join方法的字节被插入到每对字节之间
The result is returned as a new bytes object.
//结果作为一个新的bytes对象返回
Example: b'.'.join([b'ab', b'pq', b'rs']) -> b'ab.pq.rs'.*/
//这个例子非常好,说明用b''来调用join实现了字节序列合并
static PyObject *
bytes_join(PyBytesObject *self, PyObject *iterable_of_bytes)
{
return stringlib_bytes_join((PyObject*)self, iterable_of_bytes);
}
字节序列缓冲池,类似于我们前边说的浮点型空闲对象缓冲池,小整形对象缓冲池,这个可以叫做单字节对象缓冲池。cpython 内部创建单字节bytes对象时,先查看是否该字节对象已经在单字节对象缓冲池。程序刚开始运行时,单字节缓冲池是空的。随着单字节bytes对象的创建,缓冲池中才有了对象。后续再次使用时,直接从缓冲池去,避免重复创建和销毁。