Python字符串驻留机制测试结果差异原因咨询
问题背景
我正在学习Python字符串驻留机制,执行了如下测试代码:
# short str list_short_str = ['0', str(0), chr(48), ''.join(['0']), '0'.join(('','')), '230'[-1:], ''+'0'+'', 'aaa0a'.strip('a')] print("short str id:") for item in list_short_str: print(id(item)) # long str list_long_str = ['hello', 'hel'+'lo', 'helloasd'[:5], ''.join(['h','e','l','l','o']), ' hello '.strip(' ')] print("long str id:") for item in list_long_str: print(id(item))
得到输出结果:
short str id: 2450307092400 2450856182064 2450307092400 2450307092400 2450298848880 2450307092400 2450307092400 2450307092400 long str id: 2450855173808 2450855173808 2450856182256 2450856182320 2450856182192
在IDLE、PyCharm和Jupyter三种IDE中测试均得到一致结果:
- 短字符串'0'的相关测试里,
str(0)和'0'.join(('',''))的id不同,其余操作生成的字符串id均相同; - 长字符串'hello'的相关测试里,仅
'hello'与'hel'+'lo'的id相同,其余操作生成的字符串id均不同。
查阅相关资料未找到原因,恳请解答该差异产生的原因。
原因解析
Python的字符串驻留(string interning)是内存优化机制,通过复用已有字符串对象减少内存占用,但触发规则并非覆盖所有场景,核心取决于字符串是否在编译期可确定,以及运行时操作是否主动触发驻留。
一、短字符串'0'的ID差异分析
str(0)生成的字符串未被驻留str(0)是运行时调用内置函数生成的字符串,不属于编译期字面量。Python自动驻留机制默认仅对编译期可确定的字面量(如直接写的'0')、编译期可计算的表达式(如''+'0'+'')进行驻留,动态生成的结果不会自动加入驻留池,除非显式调用sys.intern()。'0'.join(('',''))生成的字符串未被驻留str.join()方法的返回结果是否驻留取决于底层实现:对于空序列拼接(两个空字符串用'0'连接),虽然结果是'0',但join是运行时动态创建新字符串的逻辑,不会主动触发驻留——毕竟join的场景多为动态拼接任意内容,Python不会默认对这类结果做驻留优化。其余操作触发驻留的原因
chr(48):返回的单个ASCII字符属于Python自动驻留范围,恰好返回'0',直接复用驻留池中的对象;''.join(['0']):当join的序列仅含一个字符串元素时,CPython底层会直接返回该元素本身,因此复用驻留池中的'0';'230'[-1:]:切片结果为单个已驻留的字面量字符,直接复用驻留池对象;''+'0'+'':属于编译期可计算的表达式,编译器会提前优化为'0',使用驻留池对象;'aaa0a'.strip('a'):strip返回的'0'是单个ASCII字符,CPython底层会复用驻留池中的已有对象。
二、长字符串'hello'的ID差异分析
'hello'与'hel'+'lo'ID相同'hel'+'lo'是编译期可计算的拼接表达式,Python编译器会在编译阶段将其合并为'hello'字面量,因此和直接写的'hello'共享同一个驻留对象。其余操作未触发驻留的原因
'helloasd'[:5]:切片是运行时动态生成字符串的操作,即使结果是'hello',也不会自动驻留——切片结果可能是任意子串,Python不会默认对这类动态生成的长字符串做驻留;''.join(['h','e','l','l','o']):join运行时动态拼接字符序列生成的字符串,不会主动加入驻留池,因此创建新对象;' hello '.strip(' '):strip运行时处理后返回的结果属于动态生成字符串,同样不会自动触发驻留,生成新对象。
补充:CPython的自动驻留规则主要覆盖:
- 编译期字面量(数字、单个字符、符合规则的短字符串)
- 编译期可计算的字符串拼接(仅当所有操作数都是字面量时)
运行时动态生成的字符串,需显式调用sys.intern()才会进入驻留池。
内容的提问来源于stack exchange,提问作者wangjianyu

