Python字符串是否不如预想的不可变?递归中+=操作出现修改冲突
问题根因说明
你观察到的现象是CPython解释器的专属实现优化导致的,和Python官方定义的字符串不可变特性并不冲突:
- Python语言规范明确约定字符串是不可变类型,所有修改字符串的操作理论上都应该返回全新的字符串实例,不会修改原实例。
- 但CPython为了提升字符串拼接性能,针对
+=操作做了特殊优化:当检测到待修改的字符串对象引用计数恰好为1,且底层存储缓冲区有足够剩余空间可扩容时,会直接在原内存地址上修改字符串内容,不会申请新内存创建新实例,这时候你看到的id就会保持不变。
递归场景出现交叉影响的原因
你的递归逻辑刚好踩到了这个优化的触发边界:
- 递归调用传递字符串的过程中,部分场景下字符串实例没有被多个作用域同时持有,满足引用计数为1的条件,就会触发原地修改
- 如果你的代码中存在其他作用域持有该字符串的引用(比如存到了外层的可变容器、或者同一层有多个变量指向该字符串),原地修改的结果就会被其他作用域读取到,从而出现你说的修改影响其他函数调用的问题
- 因为递归的调用链动态变化,字符串的引用计数也会动态变化,所以只会在部分场景下复现问题。
规避方案
- 不要依赖CPython的这个实现优化编写代码,该优化不属于Python语言标准,换用PyPy等其他解释器、或者升级/降级CPython版本都可能出现行为变化
- 如果需要确保修改字符串不会影响其他调用,拼接时主动创建新实例:把
a += b改为a = a + b,或者拼接前先做一次显式拷贝a = str(a) + b - 递归场景如果需要频繁修改字符串,建议改用
list存储字符,所有修改完成后再用''.join()转换为字符串,不仅完全不会有引用问题,性能也比字符串拼接高很多。
内容的提问来源于stack exchange,提问作者Hamster
相关产品推荐
相关产品推荐

