详解Python小数据池和代码块缓存机制

Dylan
Dylan
发布时间:2026-10-12 | 3人浏览过
原创

本文深入解析Python中的小数据池与代码块缓存机制。详细阐述int、str、bool在[-5, 256]范围内的缓存规则,以及字符串驻留(Intern)策略。对比交互式环境与IDE中整数和字符串缓存行为的差异,分析不可变对象对内存效率的影响,并提供字符串拼接的性能优化建议。

前言

本文除“总结”部分外,主要记录了对Python缓存机制的认知探索过程。关于Python 3.7.5版本中部分官方文档未明确说明的内容,目前尚未找到确切出处,如有读者知晓相关文档位置,欢迎留言补充,感谢分享!

总结:

核心规则如下: 若变量位于同一代码块内,则遵循代码块缓存机制; 若变量位于不同代码块,则遵循小数据池的驻留机制; 需特别注意,在交互式命令行中输入的每一条命令均被视为独立的代码块。

Intern(字符串驻留)机制的实现原理十分直观:系统维护一个字符串储蓄池,其底层为字典结构。在编译阶段,若字符串已存在于池中,则直接返回该已有对象,不再创建新实例;若字符串尚未加入池中,则先构造字符串对象并插入池中以供后续复用。

满足以下条件的字符串会被强制驻留: 长度为0或1的字符串; 驻留行为发生在程序编译阶段; 字符串内容仅由ASCII字母、数字及下划线组成。

1.代码块的缓存机制

Python程序由代码块构成,代码块是作为执行单元处理的Python文本片段。 常见的代码块包括:模块、函数、类定义、文件等; 在交互模式下(如cmd进入Python解释器),用户输入的每条命令均构成一个独立的代码块。

Python在执行同一代码块内初始化对象的指令时,会检查该值是否已存在。若存在,则直接重用该对象。 满足代码块缓存机制的对象在内存中仅保留一份,表现为id相同。 该机制适用的数据类型包括:int(含float)、str、bool。

具体类型表现如下: int(float):同一代码块内的任意数字均会被复用; bool:True和False在字典中分别以1和0的形式存在,且会被复用; str:同一代码块中,值相同的字符串在内存中仅存在一个实例。

s1 = 'janes@!#*ewq'
s2 = 'janes@!#*ewq'
print(s1 is s2)	 # True 

a1 = 'janes45613256132!@#$%#^%@$%' * 1
b1 = 'janes45613256132!@#$%#^%@$%' * 1
print(a1 is b1) # True

s1 = 'hah_' * 6
s2 = 'hah_' * 6
print(s1 is s2) # True

2.小数据池

Python自动对-5至256范围内的整数进行缓存。将这些整数赋值给变量时,不会创建新对象,而是直接引用已存在的缓存对象。 对于满足特定规则的字符串,Python会在字符串驻留池中创建一份副本。赋值给变量时,同样直接引用池中的对象,而非重新创建。 bool值(True/False)无论有多少变量指向它们,在内存中始终只存在一个实例。

小数据池同样仅针对int(含float)、str、bool类型。 小数据池主要用于处理不同代码块之间的对象缓存问题。

# cmd, -5~256 的小整数虽然不在同一代码块中, 但是它们适用小数据池机制
>>>a = 245
>>>b = 245
>>>a is b # True
# 长度为0与1的字符串一定会被驻留;
# 字符串驻留发生在程序编译时;
# 被驻留的字符串必须由 ASCll字母, 数字以及下划线组成;
>>>s1 = '@'
>>>s2 = '@'
>>>s1 is s2 # True

>>>s1 = ''
>>>s2 = ''
>>>s1 is s2 # True

>>>s1 = 'a_b_c'
>>>s2 = 'a_b_c'
>>>s1 is s2 # True

>>>s1 = 'a b_c'
>>>s2 = 'a b_c'
>>>s1 is s2 # False

>>>s1 = 'a_b_c' * 1
>>>s2 = 'a_b_c' * 1
>>>s1 is s2 # True

>>>s1 = 'abd_d23' * 3
>>>s2 = 'abd_d23' * 3
>>>s1 is s2 # True

>>>a, b = "some_thing!", "some_thing!"
>>>a is b # False

>>>a, b = "some_thing", "some_thing"
>>>a is b # True
a1 = 1000
b1 = 1000
a1 is b1 # True

class C1(object): 
   a = 100
   b = 100
   c = 1000
   d = 1000
 
 
class C2(object):
   a = 100
   b = 1000

print(C1.a is C1.b)  # True
print(C1.a is C2.a)  # True
print(C1.c is C1.d)  # True
print(C1.c is C2.b)  # False

3.优缺点

优点:对于值相同的字符串(如标识符),可直接从池中获取,避免了频繁的创建和销毁操作,从而提升运行效率并节约内存空间。

缺点:字符串拼接或修改操作会影响性能。 由于字符串是不可变对象,修改操作并非就地(inplace)进行,而是需要创建新对象。这也是为什么在拼接多个字符串时,推荐使用join()而非+运算符的原因。 join()方法会预先计算所有字符串的总长度,然后一次性拷贝内容并仅创建一次对象。

小整数对象池

为避免整数频繁申请和销毁内存,Python引入了小整数对象池。Python将[-5, 256]范围内的整数定义为小整数,这些对象在启动时已预先创建,且不会被垃圾回收。 在任意Python程序中,无论小整数出现在LEGB作用域的哪个层级,只要数值在此范围内,所有引用均指向同一个对象。

# 3.7.5, ipython7.18.1
a = -5
b = -5
a is b # True

a = -6
b = -6
a is b # False

a = 256
b = 256
a is b # True

a = 257
b = 257
a is b # Flase

大整数对象池

在cmd终端中,每次赋值大整数时,通常会重新创建对象; 而在PyCharm等IDE中,所有代码在运行时加载到内存中,被视为一个整体,因此存在一个大整数对象池,使得同一代码块内的大整数成为同一对象。 变量c和d处于同一代码块,因此相等;而C1.b和C2.b分别属于各自的类定义代码块,因此不相等。

# cmd 终端
a = 1000
b = 1000
a is b # False
--------------------
class C1(object): 
   a = 100
   b = 100
   c = 1000
   d = 1000
 
 
class C2(object):
   a = 100
   b = 1000

print(C1.a is C1.b)  # True
print(C1.a is C2.a)  # True
print(C1.c is C1.d)  # True ?? 难道 cmd 中也有大整数池 ?? 类加载的时候是在一块内存中,同值同地址 ?? 
print(C1.c is C2.b)  # False

# pycharm 等编辑器中
a = 1000
b = 1000
a is b # True
--------------------
class C1(object): 
   a = 100
   b = 100
   c = 1000
   d = 1000
 
 
class C2(object):
   a = 100
   b = 1000

print(C1.a is C1.b)  # True
print(C1.a is C2.a)  # True
print(C1.c is C1.d)  # True
print(C1.c is C2.b)  # False

字符串驻留机制

Python解释器为提高字符串使用效率,在编译时采用了Intern(字符串驻留)技术。该机制确保值相同的字符串对象仅保存一份,并置于共享的字符串储蓄池中。由于字符串必须是不可变对象(整数同理),因此浮点数无法采用此机制。

简单原理:

Intern机制通过维护一个字典结构的字符串储蓄池来实现。编译时,若字符串已存在,直接返回;若不存在,则构造并加入池中以供后续获取。 然而,解释器内部对Intern策略的使用有特定考量。某些场景下会自动启用Intern,而另一些场景则需要手动触发。具体见下文常见情景分析。

# cmd 中浮点数没有被缓存
a = 1.0
b = 1.0
a is b # False

# cmd 中并非全部的字符串都会采用intern机制; 仅 包括下划线、数字、字母的字符串才会被 intern--类标识符
s1="hello"
s2="hello"
s1 is s2 # True

# 如果有空格,默认不启用intern机制
s1="hell o"
s2="hell o"
s1 is s2 # False

s1 = "hell!*o"
s2 = "hell!*o"
print(s1 is s2) # False

# 如果一个字符串长度超过20个字符,不启动intern机制 -- 看网上很多都是这么写的, 不超过二十个就为真,但是我在自己 3.7/8.5 版本上试了一下,发现好像没有限制,不知道是 Python 更新了,还是什么问题……
s1 = "a" * 20
s2 = "a" * 20
s1 is s2 # True

s1 = "a" * 21
s2 = "a" * 21
s1 is s2 # True

s1 = "ab" * 10
s2 = "ab" * 10
s1 is s2 # True

s1 = "ab" * 11
s2 = "ab" * 11
s1 is s2 # True

# 'kz' + 'c' 编译时已经变成 'kzc',而 s1 + 'c' 中 s1 是变量, 会在运行时进行拼接,所以没有被intern?
'kz' + 'c' is 'kzc' # True

s1 = 'kz'
s2 = 'kzc'
s1+'c' is 'kzc' # False

# pycharm 等编辑器中,只要是同一个字符串,都为 True,并不用是下划线、数字、字母的字符串
s1 = "hell o"
s2 = "hell o"
print(s1 is s2) # True

s1 = "hell!*o"
s2 = "hell!*o"
print(s1 is s2) # True

s1 = "a" * 20
s2 = "a" * 20
print(s1 is s2) # True

s1 = "a" * 21
s2 = "a" * 21
print(s1 is s2) # True

s1 = "ab" * 10
s2 = "ab" * 10
print(s1 is s2) # True

s1 = "ab" * 11
s2 = "ab" * 11
print(s1 is s2) # True

'kz' + 'c' is 'kzc' # True

s1 = 'kz'
s2 = 'kzc'
s1+'c' is 'kzc' # False

# 编辑器中,float 也被缓存了
a = 1.0
b = 1.0
a is b

以上就是详解Python小数据池和代码块缓存机制的详细内容,更多关于Python小数据池和代码块缓存机制的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表