bytes 对象是不可变对象,那么根据我们对浮点数的了解,可以大胆猜测 bytes 对象也有自己的缓存池。
// Include/internal/pycore_global_objects.h
struct _Py_static_objects {
struct {
// 该结构体保存了一些静态分配的对象,它们都是单例的
// 小整数对象池
PyLongObject small_ints[_PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS];
// 空的 bytes 对象
PyBytesObject bytes_empty;
// bytes 对象缓存池
struct {
PyBytesObject ob;
char eos;
} bytes_characters[256];
// ...
} singletons;
};
结构体里的 bytes_characters 便是 bytes 对象的缓存池,这是一个长度为 256 的数组,这意味着缓存池里面可以容纳 256 个 bytes 对象。
但 _Py_static_objects 保存的是静态分配的单例对象,也就是说在解释器启动之后这些对象就已经被初始化好了,所以 bytes 对象缓存池和小整数对象池一样,里面存储的对象是固定的。
小整数对象池里面存储的是 -5 ~ 256 之间的整数,而 bytes 对象缓存池里面存储的是 256 个单字节 bytes 对象,即 b"\x00"、b"\x01"、····、b"\xff"。所以解释器对长度为 1 的 bytes 对象是做了优化的,它们只会存在一份,因为一个字节的最大值为 255,所以 bytes_characters 里面保存了 256 个 bytes 对象。
当然以上都只是结论,我们来实际看一下初始化的过程。
// Include/internal/pycore_runtime_init.h
#define _PyRuntimeState_INIT(runtime) \
{ \
/* ... */ \
.static_objects = { \
.singletons = { \
.small_ints = _Py_small_ints_INIT, \
.bytes_empty = _PyBytes_SIMPLE_INIT(0, 0), \
.bytes_characters = _Py_bytes_characters_INIT, \
}, \
}, \
._main_interpreter = \
_PyInterpreterState_INIT(runtime._main_interpreter), \
}
runtime 保存了程序运行时的环境和行为,比如内存分配、异常处理、GIL 状态、以及静态单例对象等等,当然还有进程状态对象。