Python 3字符串存储、拼接与切片的实现原理及性能疑问
Python字符串随机访问速度快的原因
首先明确:CPython的字符串(str类型)底层并不使用可变长度的UTF-8存储,而是采用固定宽度码元的灵活存储方案,这是它O(1)时间复杂度随机访问字符的核心原因。
- CPython 3.3之后引入了「紧凑字符串(Compact String)」优化,会自动根据字符串包含的最高Unicode码点选择最小的固定存储单元:
- 所有字符码点≤255(Latin-1范围):每个字符占1字节
- 存在字符码点在256~65535之间:每个字符占2字节
- 存在字符码点≥65536(比如你测试用的😊表情):每个字符占4字节
你测试的字符串包含4字节的emoji字符,所以整个字符串每个字符都固定占4字节,访问第N个字符只需要直接计算内存偏移量:字符串起始内存地址 + N * 4,不需要遍历统计字符长度,所以不管是头部、中间还是尾部字符,访问速度理论上完全一致,都是O(1)。
- 你观察到的首字符访问稍慢是基准测试的常见缓存误差:第一次访问字符串数据时,对应的内存页还没加载到CPU缓存中,会触发缓存缺失(Cache Miss),耗时更高;后续访问中间、尾部字符时,对应内存已经加载到缓存中,耗时更低。你可以调整访问顺序,先测尾部、再中间、再头部,就会发现第一次访问的位置耗时最高,和是否是首字符无关。
- 字符串join操作耗时符合预期:
str.join()需要先遍历整个字符列表统计总长度,申请对应大小的连续内存,再逐个复制字符,1000万量级的元素操作耗时0.2秒左右是正常水平。
补充说明:如果是把str编码成UTF-8格式的bytes对象,那存储就是可变长度的,对bytes取下标只能拿到单个字节,要从UTF-8 bytes中取第N个Unicode字符,才会需要和PHP的mb_substr一样遍历字符统计长度,耗时会随偏移量增大而上升。
内容的提问来源于stack exchange,提问作者kaan_atakan
相关产品推荐
相关产品推荐

