Python:对象缓存优化机制(CPython)

发布于:2025-08-15 ⋅ 阅读:(20) ⋅ 点赞:(0)

在 Python 的世界中,性能优化并不仅仅依赖开发者编写高效的代码。CPython(用 C 语言实现的 Python 解释器,最常用版本)也在底层对常见对象进行了精妙的缓存优化。这些优化手段不但提高了性能,还减少了内存占用。

一、小整数缓存机制

CPython 在启动时会预先创建并缓存 -5 ~ 256 范围内的整数对象。这些对象在整个运行期间都是单例,也就是说多个变量引用它们时,会共享同一块内存。

a = 100b = 100print(a is b)  # True

为什么是这个范围?这主要是出于性能考虑。小整数在实际应用中频繁出现,如循环计数、列表索引、状态标识等,缓存可以显著提高执行效率。

CPython 源码(Objects/longobject.c)中定义如下:

#define NSMALLPOSINTS 257  // 0 到 256#define NSMALLNEGINTS 5    // -1 到 -5

对于超出这个范围的整数(如 1000),每次创建的对象是不同的:

a = 1000b = 1000print(a is b)  # False(通常)

注意:

某些解释器(比如交互式解释器、Jupyter Notebook、PyCharm 控制台)可能会因为常量合并优化,使得 a is b 成为 True,这不是标准行为,不可依赖。

二、字符串驻留机制

CPython 对一部分字符串也进行了缓存,称为“字符串驻留”(string interning)。如果多个变量持有的是同一个驻留字符串,它们就会共享内存地址。

哪些字符串会被驻留:

1、所有长度为 0 或 1 的字符串,如:""(空字符串)、"a"(单字符字符串)等。

2、所有只包含字母、数字和下划线的标识符样式字符串(即合法的变量名样式),如:"hello"、"var_1" 等。

标识符样式字符串是在编译阶段自动驻留的。

3、显式调用 sys.intern() 的字符。

a = "hello"    # 标识符样式字符串b = "hello"print(a is b)  # True (被驻留)
a = "hello world"    #含空格或标点的字符串b = "hello world"print(a is b)  # False(不是合法的变量名样式,也可能被解释器优化,结果为 True)
a = "a"    # 单字符字符串b = "a"print(a is b)  # True(长度为 1)

可以使用 sys.intern() 手动强制驻留字符串:

import sys
a = sys.intern("hello world")b = sys.intern("hello world")print(a is b)  # True

提示:

字符串驻留的行为在不同 Python 版本之间有所不同,不能完全依赖。

三、单例对象的共享

Python 内置了几个全局唯一的单例对象,它们在内存中只存在一份,并在整个程序中复用。

None,空值。

True / False,布尔值。

... ,Python 内置对象,类型为 EllipsisType,常用作占位符或切片中的缩略表示。

NotImplemented,用于算术操作不支持时的返回值。

a = Noneb = Noneprint(a is b)  # True
a = ...b = ...print(a is b)  # True

这些对象在 CPython 启动时就已经创建,并且始终只存在一份。

四、空的不可变容器缓存

CPython 还缓存了某些空的不可变对象,例如:

a = ()b = ()print(a is b)  # True 因为空元组是缓存的
a = ""b = ""print(a is b)  # True 空字符串也是缓存的

这也是一种内存优化手段,因为空的不可变对象经常出现,完全可以共享使用。

要的注意的是,可变对象不会缓存:

print([] is [])   # Falseprint({} is {})   # False

五、补充说明

1、不要将空元组(())的行为套用在 frozenset 上,空的 frozenset() 并不会被缓存,即使内容相同,每次创建的对象是不同的。

a = frozenset()b = frozenset()print(a is b)  # False

早期 CPython 某些版本/交互式解释器中,有可能因为编译器优化产生缓存效果,但这不是规范行为,不可靠。

2、不要滥用 is 来比较值

is 是用来判断对象身份,而不是比较值。比较数值时应使用 ==。

# 正确 if a == 1000:    ...
# 不推荐if a is 1000:    ...

使用 is 的正确场景是:比较单例对象(如 None、True、False)。

图片

“点赞有美意,赞赏是鼓励”


网站公告

今日签到

点亮在社区的每一天
去签到