在 Python 中,字符串 (str) 是不可变对象,这意味着创建后无法更改其内容。任何修改字符串的操作(例如,连接或替换字符)都会创建一个新的字符串对象。这保证了工作安全性和可预测性,因为您不必担心其他部分的代码会隐式地更改字符串。
s = 'Hello' s2 = s.replace('H', 'J') # s 仍然是 'Hello',而 s2 将是 'Jello'
与字符串不同,Python 中的列表是可变的。其内容可以通过索引或方法就地更改,这在不同的地方使用同一个列表时有时会导致隐式效果。
从性能角度来看:如果需要频繁修改大字符串(例如,在循环中),不可变性机制可能会导致过多的内存分配和代码运行缓慢。在这种情况下,推荐使用列表来积累字符串片段,然后通过 ''.join() 进行连接。
示例:
# 差(对于大数据量来说慢): s = '' for word in words: s += word # 每一步都会创建一个新的字符串 # 好: parts = [] for word in words: parts.append(word) s = ''.join(parts)
问题: 为什么下面的代码 "s += 'abc'" 比 "s = s + 'abc'" 更快?
答案: 这样的问题是为了检查一个人是否理解这两种操作对字符串实际上是等效的(s += 'abc' 创建一个新的对象,就像 s = s + 'abc' 一样)——这是 Python 中类型行为的方式。对于列表,行为则不同,因为 list += [...] 会改变对象,而 list = list + [...] 会创建一个新的。对于字符串来说,这总是一个新的字符串。
s = 'hi' s += 'abc' # 新对象,原始字符串不变 def compare(s): a = s a += 'abc' # id(a) != id(s) <-- 内存中不同的对象
故事
在一个需要处理大日志(处理数百兆长度字符串)的项目中,开发人员在循环中使用了天真的字符串连接。结果 — 性能出现巨大下滑,内存使用快速增长。通过列表和 join() 优化后,执行时间减少了 20 倍。
故事
在一个项目中,当尝试通过索引 "修复" 字符串中的字符时,程序员期望看到原始字符串的变化。出现了错误 TypeError: 'str' object does not support item assignment。经过几个小时,调试人员不得不通过切片创建新的字符串并替换所需字符。
故事
在将字符串传递给函数以 "补充" 它们(例如,将后缀添加到列表中每个元素)时,一位开发人员期望字符串会 "就地" 更改。结果 — 函数返回 None(由于缺少 return),所有字符串保持原样。