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

在那个世界里头, 性能优化可不是仅仅靠着开发者写出高效代码就行的。用 C 语言实现的那个解释器, 它的那个最为常用的版本, 在底层针对常见对象做了精巧的缓存优化。这些优化的办法, 不但使得性能得以提高, 而且还把内存占用给减少了。

一、小整数缓存机制

启动的时候, 会预先去创建, 并且缓存处于 -5 到 256 范围之内的整数对象。这些对象在整个运行的期间都是单例, 这意味着当多个变量引用它们的时候, 会去共享同一块内存。

复制代码
a = 100
b = 100
print(a is b)  # True

为什么会是这个范围呢, 这主要是基于性能方面的考虑, 小整数在实际运用当中会频繁地出现, 像是循环计数、列表索引、状态标识等情况, 缓存能够显著地提升执行效率。

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

复制代码
#define NSMALLPOSINTS 257  // 0 到 256
#define NSMALLNEGINTS 5    // -1 到 -5

是针对超出这个范围的那些整数而言的 , 像1000这样的 , 每次所创建的对象是不一样的 , 是有区别的 , 是各不相同的:

复制代码
a = 1000
b = 1000
print(a is b)  # False(通常)

注意:

一些(像交互式解释器、控制台这样的)解释器, 有可能因常量合并优化, 致使 a is b 变为 True, 这并非标准行为, 不能予以依赖。

二、字符串驻留机制

对一部分字符串开展了缓存操作, 这被称为"字符串驻留"()。要是多个变量所持有乃是同一个驻留字符串, 那么它们将会实现内存地址的共享。

哪些字符串会被驻留:

1、那些长度是 0 或者 1 的字符串, 像: ""(此为空字符串)、"a"(这是单字符字符串)等等。

2、仅由字母、数字以及下划线所构成的标识符样式字符串, 也就是合法的变量名样式, 像这种, 比如: "hello"、"var_1" 等等等等这类的。

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

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

复制代码
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.() 手动强制驻留字符串:

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

提示:

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

三、单例对象的共享

嵌入了几个在全局范围内独一无二的单例对象, 并在内存里仅仅存在个别一份, 且在整个程序进程中进行再度使用且不间断。

None,空值。

True / False,布尔值。

有着内置对象, 其类型是这样的这般, 常常被用作占位符, 或者是切片里的简略表示形式, 是这样的情况。

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

复制代码
a = None
b = None
print(a is b)  # True

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

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

四、空的不可变容器缓存

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

复制代码
a = ()
b = ()
print(a is b)  # True 因为空元组是缓存的

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

这同样是一种用于内存优化的办法, 鉴于空的不能改变的对象时常会出现, 所以是能够共同分享去使用的。

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

复制代码
print([] is [])   # False
print({} is {})   # False

五、补充说明

1、不要把、套用在、关于空元组(())行为, 空的 () 不会被缓存, 就算内容一样每次创建的对象与先前不同。

复制代码
a = frozenset()
b = frozenset()
print(a is b)  # False

在早期的时候, 于某些版本当中, 或者是交互式解释器里面, 存在着这样一种可能性, 那就是会因为编译器进行优化从而出现缓存的效果, 然而这并不是规范所规定的行为, 并且是不可靠的。

2、不要滥用 is 来比较值

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

复制代码
# 正确 
if a == 1000:
    ...
# 不推荐
if a is 1000:
    ...

正确场景是使用is的情况为: 去比较单例对象, 像None、True、False这些。

相关推荐
枫彩2 小时前
A股数据源怎么选?用 Python 验收 AI 复盘的日期与明细
python·ai agent·股票数据·mcp
2601_962382432 小时前
Python 代码不可不知的函数式编程技术
python·函数式编程·高阶函数·嵌套函数·头等函数
2601_962078032 小时前
Python办公自动化与数据分析实战 培训班2021年
python·数据分析·办公自动化·自动化办公·实战演练
用户938515635072 小时前
Text2SQL 实战:用 DeepSeek 大模型实现自然语言查询 SQLite 数据库
python·sqlite
2601_962099683 小时前
Python环境下中文分词实现与应用探索
python·自然语言处理·中文分词·jieba·开源社区
2601_966949653 小时前
批量获取多只股票五档盘口的极简方案:QuantDash Python SDK 实战指南
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
用户019027581613 小时前
如何用 Python 监控 A 股涨停跌停与封板/炸板?
python
Lyra_Infra3 小时前
云效主机部署场景下 Python 服务生命周期问题复盘
后端·python
刀鋒偏冷3 小时前
CentOS 7 配置 Python 3.12.4 + RTX 4090 深度学习环境全记录
python·深度学习·centos