字符串不可变是设计选择而非技术限制,旨在保障哈希稳定性、字符串驻留和线程安全;所有“修改”操作均返回新对象,原字符串内容与地址不变。

字符串不可变是设计选择,不是技术限制
Python 的 str 类型被明确定义为不可变序列类型,所有看似“修改”的操作(如 replace()、upper()、+)都返回新对象,原对象内容与内存地址完全不变。这不是 CPython 实现偷懒,而是为了支撑哈希稳定性、字符串驻留、线程安全这三件关键事——比如字典键一旦写入就不能变哈希值,否则 dict["key"] 下次就查不到;又比如 a = "x"; b = "x" 能共享内存,前提是没人能偷偷改 a[0]。
循环拼接字符串时 += 会显著变慢
常见错误现象:s = "" 后在 for 循环里反复执行 s += char,10⁴ 次就可能比预期慢 10 倍以上。原因在于每次 += 都要分配新内存、复制旧内容、再追加,时间复杂度接近 O(n²)。
- 正确做法:用
list缓存片段,最后" ".join(parts) - 片段少且固定(≤3 个)时,直接
"a" + "b" + "c"更清晰,性能差异可忽略 - 流式构建(如生成 HTML 或日志行)建议用
io.StringIO,但别忘了调用.getvalue()
str.replace() 链式调用会创建多个中间字符串
像 s.replace("a", "x").replace("b", "y") 这种写法,实际产生三个字符串对象:原始串 → 第一次替换结果 → 最终结果。每一步都复制全部字符。
- 能合并就合并:用
re.sub(r"[ab]", lambda m: {"a":"x","b":"y"}[m.group()]) - 固定子串替换优先用
str.translate(),它只遍历一次,速度最快 - 正则模式若重复使用,务必预编译:
pattern = re.compile(r"\d+"),避免每次调用都解析
误以为“变量变了”就是“字符串变了”
典型陷阱:def mutate(s): s += "!" 调用后外部变量没变。这不是传值或传引用的问题,而是 s += "!" 本质是 s = s + "!" —— 给局部变量重新赋值,不影响调用方的绑定关系。
- 需要返回新值,就必须显式
return s + "!" - 列表里存字符串,
lst[0] += "!"是合法的,但它只是把lst[0]这个索引位置重新指向新字符串,不是改原字符串内容 - 真要高频修改文本(如解析器缓冲区),用
bytearray或array.array('u'),但它们不兼容str接口,不能直接传给open()或json.dumps()
str 当成类似 C 的字符数组来用。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











