Python不可变对象的身份特性及相关技术问询
Python中不可变对象的单例行为测试
基于Python 3.10.13(运行于Ubuntu Linux系统)执行以下测试:
整数的单例特性
已知Python中部分小整数为单例,但大整数并非如此:
>>> a = 1 >>> b = 1 >>> a is b True >>> a = 1000000 >>> b = 1000000 >>> a is b False
浮点数的身份特性
所有浮点数的身份似乎互不相同:
>>> a = 0.0 >>> b = 0.0 >>> a is b False
字符串的身份差异
原本以为每个字符串的身份也各不相同,但实际测试发现:
>>> a = "1024" >>> b = "1024" >>> a is b True
然而:
>>> a = str(2**10) >>> b = str(2**10) >>> a is b False
元组的身份特性
元组与浮点数类似,身份互不相同:
>>> a = (1,2,3) >>> b = (1,2,3) >>> a is b False
问题解答
1. 上述行为是Python的刻意设计,还是实现过程中无关紧要的附带结果?
这些行为大多是CPython实现层面的优化附带结果,并非Python语言规范强制要求的设计。Python标准仅明确is用于比较对象身份、==用于比较值相等,但没规定不可变对象必须复用实例。不过小整数的单例是CPython的刻意优化——小整数在代码中出现频率极高,预存复用能减少内存占用、提升运行效率。
2. 若为实现附带结果,为何字符串与元组、浮点数的表现不同,且字符串的身份还取决于创建方式?
- 字符串的差异:CPython有「字符串驻留(string interning)」优化,会自动复用符合规则的字符串实例。比如直接用字面量定义的字符串(如
"1024"),如果只包含字母、数字、下划线,会被加入驻留池复用;但通过str()这类函数动态生成的字符串,除非显式调用sys.intern(),否则不会自动驻留,因此会生成新实例。 - 浮点数与元组:浮点数的数值空间极大,无法像小整数那样预存所有常用值;而元组即使内容相同,创建时也默认生成新实例——因为元组可以包含可变对象,预存复用的收益极低,还可能引发意外问题,所以CPython没做这类优化。
3. 除使用==比较不可变对象外,还有哪些场景需要注意该行为?
- 判断内置单例对象:
None、True、False是Python内置的单例,用is判断是安全且推荐的,比如if x is None:。 - 函数默认参数:如果依赖不可变对象的身份判断默认参数,可能踩坑——比如有人错误用
is判断默认传入的字符串是否为初始值,结果因为驻留机制导致判断逻辑出错。 - 自定义缓存逻辑:自己实现对象缓存时,要注意CPython的自动驻留/单例机制可能干扰预期,比如误以为生成了新实例,实际却复用了旧的。
- 内存调试分析:排查内存泄漏时,要注意单例/驻留对象的存在,避免误判对象的实际创建次数。
内容的提问来源于stack exchange,提问作者doetoe
相关产品推荐
相关产品推荐

