字符串驻留是cpython运行时优化,非语法特性;仅符合标识符规则且编译期确定的字面量自动驻留,其余需显式调用sys.intern(),误用可能导致内存泄漏或比较错误。

字符串驻留不是语法特性,而是CPython的运行时优化
Python不会对所有字符串自动驻留,它只在特定条件下复用对象。这个行为由CPython解释器内部维护的 interned 字典控制,本质是字典查表:键是字符串值,值是其内存地址。你调用 sys.intern() 或触发编译期驻留时,解释器才往这个字典里写入或查找。
这意味着:同一段代码在交互式环境(逐行编译)和脚本文件(整块编译)中,a is b 的结果可能不同;而跨模块、动态生成的字符串(如 "a" + "b")默认不驻留——除非显式调用 sys.intern()。
哪些字符串会被自动驻留?看命名规则和编译时机
CPython对满足以下条件的字符串做**隐式驻留**:
- 内容符合 Python 标识符规则:以字母或下划线开头,后续只含字母、数字、下划线(例如
"user_id"、"_cache") - 在源码中作为字面量直接出现(不是拼接、格式化、
input()输入等运行时构造) - 出现在编译阶段可确定的位置,比如变量赋值、函数名、类名、模块级常量
反例:"hello world"(含空格)、"123abc"(数字开头)、"a" + "b"(运行时拼接)都不会被自动驻留。你可以用 id() 或 is 验证:
import sys a = "hello_world" b = "hello_world" print(a is b) # True —— 符合标识符规则,自动驻留 <p>c = "hello world" d = "hello world" print(c is d) # False —— 含空格,不驻留</p>
手动驻留要用 sys.intern(),但别滥用
sys.intern() 是唯一可靠的显式驻留方式,适用于你知道会高频重复、且需快速身份比较的场景(比如解析 CSV 列名、HTTP 头字段、数据库字段名)。
但它有代价:
- 每次调用都要查
interned字典,是哈希查找,但仍有开销 - 驻留后的字符串永不释放(直到解释器退出),内存只增不减
- 多线程下安全,但频繁调用可能成为瓶颈
典型误用:对每个 line.strip() 都 sys.intern()。正确做法是只对有限集合(如预定义的字段名列表)驻留:
import sys
COLUMNS = ["name", "age", "city"]
INTERNED_COLS = {col: sys.intern(col) for col in COLUMNS}
<h1>解析时</h1><p>for line in lines:
cols = line.split(",")
if sys.intern(cols[0]) is INTERNED_COLS["name"]: # 快速身份判断
...</p>
is 比较字符串前,先确认是否真被驻留了
拿 is 当字符串相等判断用,等于埋雷。它只在双方都指向同一驻留对象时返回 True,否则就是 False,哪怕内容一模一样。
容易踩坑的地方:
- 从文件/网络读入的字符串永远不会自动驻留,
line.rstrip() is "END"几乎总为False - 使用
f-string、.format()、%拼出的字符串也不会驻留 -
==始终安全,is只适合已知驻留前提下的性能敏感路径
最稳妥的习惯:默认用 ==;只有当你明确控制了字符串来源(比如全来自 sys.intern() 或全是合法标识符字面量),再考虑用 is 加速。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











