Python小整数对象池和字符串intern实例解析


Posted in Python onMarch 21, 2020

is用于判断两个对象是否为同一个对象,具体来说是两个对象在内存中的位置是否相同。

python为了提高效率,节省内存,在实现上大量使用了缓冲池技术和字符串intern技术。

整数和字符串是不可变对象,也就意味着可以用来共享,如100个“python”字串变量可以共享一个“python”字符串对象,而不是创建100个“python”字符串。

小整数对象池

为了应对小整数的频繁使用,python使用对小整数进行了缓存,默认范围为[-5,256],在这个范围内的所有整数被python完全地缓存,当有变量使用这些小整数时,增加对应小整数对象的引用即可。

>>> i = -5
>>> j = -5
>>> i is j # i和j是同一个对象
True
>>> i = 256
>>> j = 256
>>> i is j # i和j是同一个对象
True
>>> i = 257
>>> j = 257
>>> i is j # i和j是不同对象
False

由上面的实例可以看到,当变量在[-5,256]之间时,两个值相同的变量事实上会引用到同一个小整数对象上,也就是小整数对象池中的对象,而不会去创建两个对象。而当变量超出了这个范围,两个值相同的变量也会各自创建整数对象,所以两者对应的对象不同。

字符串intern

如果当前变量引用的字符串对象已经存在的话,直接增加对应字符串对象的引用,而不去创建新的字符串对象,这就是字符串intern机制。

 >>> i = "12"
 >>> j = "12"
 >>> i is j
 True

在详细探讨字符串intern机制之前,先看一个奇怪的问题:

 >>> i = "1 2"
 >>> j = "1 2"
 >>> i is j
 False

 i = "1 2"
 j = "1 2"
 print(i is j)

输出结果

 True

上述代码分开运行,结果为False,但是合在一起结果却为True,也就是说分开运行的时候,i,j指向不同对象,而合在一起的时候i,j却指向了相同对象。为了明白其中的缘由,需要简单理解python的编译机制。

编译机制

在python中,万物皆对象,包括代码本身也是一种对象。python用code对象表示代码,代码编译后产生code对象。通常一个作用域对应一个code对象。

i = "1 2"
j = "1 2"
print(i is j)

def f():
  pass

编译结果

2 0 LOAD_CONST 0 ('1 2')
2 STORE_NAME 0 (i)

3 4 LOAD_CONST 0 ('1 2')
6 STORE_NAME 1 (j)

5 8 LOAD_CONST 1 (<code object f at 0x00000200F257CF60, file "small_int.py", line 5>)
10 LOAD_CONST 2 ('f')
12 MAKE_FUNCTION 0
14 STORE_NAME 2 (f)
16 LOAD_CONST 3 (None)
18 RETURN_VALUE

Disassembly of <code object f at 0x00000200F257CF60, file "small_int.py", line 5>:
6 0 LOAD_CONST 0 (None)
2 RETURN_VALUE

上述代码中编译生成了两个code对象,一个代表全局作用域,另一个代表函数f。

code对象保存了变量,常量(常量字面量)以及编译结果。code对象用常量表来保存常量,考虑到一个常量可能出现多次,在一张表上保存一个常量多次太过于奢侈。所以code对象对每个常量只保存一次,在需要引用它的地方使用它在常量表的位置作为常量的表示。在上述编译结果中可以看到,"1 2"这个字符串常量使用了两次,编译的代码为"LOAD_CONST 0",这里的0就是"1 2"在常量表当中的位置。

由于编译的这个特性,在同一个code对象中的变量,如果它们引用了同一个常量,那么无论这个常量有没有缓冲机制,它们引用的都是同一个对象。

a = "12"
b = "12"
c = "1 2"
d = "1 2"
e = 257
f = 257
g = 2424234234234234
h = 2424234234234234
print(a is b, c is d, e is f, g is h)

输出结果

 True True True True

这个例子说明,在同一个code对象当中,常量(字面量)仅一份,这与缓冲机制无关,是编译特性。所以对于上述那个奇怪的问题就可以解释了,当i,j在同一个code对象(同一个作用域)中引用常量"1 2",它们引用的都是同一个对象。而当在python命令行中分开执行时,对于每一条语句,都是一个单独的code对象,这时起作用的是字符串intern机制,上述运行结果说明,字符串intern机制对"12"进行了intern,而对"1 2"没有进行intern。

编译机制与小整数对象池对比

i = 257
j = 257
a = i - 1
b = i - 1
c = i + 1
d = i + 1
print(i is j, a is b, c is d)

输出结果

 True True False

i和j引用同一个常量,这是编译机制,所以i与j指向同一个整数对象,后面a和b虽然相等,但不引用常量,此时启用小整数对象池,a,b都等于256,在对象池中,所以a,b引用同一个对象,后面c,d不在对象池中,所以两者对象不同。

这里有一点需要注意,没有变量参与的运算会被编译器直接优化成对应的常量,进而保存进常量表中。

字符串intern机制与字符缓冲池
在编译过程中,字符串intern机制将所有的变量名进行intern,但对常量进行的intern有一点特殊的限制。能够intern的常量必须只包含[a-zA-Z0-9_],即字母数字加下划线,如果含有其他字符,就不会intern。在运行过程中,通过计算得到的字符串不会intern。

字符串有一个和小整数对象池相似的字符缓冲池,用于在运行过程中缓存单个字符,所以计算得到的字符串虽然不会intern,但如果是单个字符,就会使用到字符缓冲池。

k = "bbb"
a = k[0]
b = k[0]
c = k[1:]
d = k[1:]
print(a, d)
print(a is b, c is d)

输出结果

 b bb
 True False

可以看到,a和b确实指向同一个对象,而c和d指向不同对象,这就是字符缓冲池。

编译机制与字符串intern对比

i = "1 2"
j = "12"
k = "__fjdslfjaskfas"

ii = "1 2"
jj = "12"
kk = "__fjdslfjaskfas"

def f():
  a = "1 2"
  b = "12"
  c = "__fjdslfjaskfas"
  return a is i, b is j, c is k

print("Code:", i is ii, j is jj, k is kk)
print(f"intern: {f()}")

输出结果

 Code: True True True
 intern: (False, True, True)

i包含空格,包含空格的常量不会被intern,而其他两个常量不包含其他字符,所以会被intern。

总结

1. python代码被编译成code对象,通常一个code对象对应于一个作用域,作用域中重复出现的变量名以及常量在code中只保存一次。

2. 字符串intern机制主要作用于编译过程,在编译收集完变量和常量时,对变量和常量进行intern,而后构建一个code对象。

3. 字符串intern对常量的intern有限制,能够intern的常量必须只包含[a-zA-Z0-9_],即字母数字加下划线,如果含有其他字符,就不会intern。

4. 小整数对象池和字符缓冲池都是作用于运行过程中,python缓存小的整数和字符,当有变量使用这些对象时,不用额外创建对象。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python脚本在Appium库上对移动应用实现自动化测试
Apr 17 Python
python中argparse模块用法实例详解
Jun 03 Python
Python实现快速多线程ping的方法
Jul 15 Python
Python爬虫爬取新浪微博内容示例【基于代理IP】
Aug 03 Python
详解python使用turtle库来画一朵花
Mar 21 Python
如何不用安装python就能在.NET里调用Python库
Jul 12 Python
Python实现基于SVM的分类器的方法
Jul 19 Python
PYTHON EVAL的用法及注意事项解析
Sep 06 Python
python求质数列表的例子
Nov 24 Python
Python如何定义有可选参数的元类
Jul 31 Python
python 基于PYMYSQL使用MYSQL数据库
Dec 24 Python
python利用proxybroker构建爬虫免费IP代理池的实现
Feb 21 Python
Python描述符descriptor使用原理解析
Mar 21 #Python
Python如何省略括号方法详解
Mar 21 #Python
Python如何使用bokeh包和geojson数据绘制地图
Mar 21 #Python
Spring Boot中使用IntelliJ IDEA插件EasyCode一键生成代码详细方法
Mar 20 #Python
python+opencv实现移动侦测(帧差法)
Mar 20 #Python
Java Spring项目国际化(i18n)详细方法与实例
Mar 20 #Python
Python 自由定制表格的实现示例
Mar 20 #Python
You might like
一个PHP缓存类代码(附详细说明)
2011/06/09 PHP
PHP的关于变量和日期处理的一些面试题目整理
2015/08/10 PHP
PHP基于面向对象实现的留言本功能实例
2018/04/04 PHP
php微信开发之关注事件
2018/06/14 PHP
jquery绑定原理 简单解析与实现代码分享
2011/09/06 Javascript
jQuery 选择器项目实例分析及实现代码
2012/12/28 Javascript
一个JavaScript去除字符串末尾的空白实例代码
2014/09/22 Javascript
javascript运动详解
2015/07/06 Javascript
常见JS验证脚本汇总
2015/12/01 Javascript
jQuery实现表格文本框淡入更改值后淡出效果
2016/09/27 Javascript
jQuery插件HighCharts实现的2D对数饼图效果示例【附demo源码下载】
2017/03/09 Javascript
JavaScript中的FileReader图片预览上传功能实现代码
2017/07/24 Javascript
JavaScript获取用户所在城市及地理位置
2018/04/21 Javascript
vue debug 二种方法
2018/09/16 Javascript
详解jQuery获取特殊属性的值以及设置内容
2018/11/14 jQuery
Vue infinite update loop的问题解决
2019/04/23 Javascript
微信小程序获取用户信息并保存登录状态详解
2019/05/10 Javascript
js实现随机div颜色位置 类似满天星效果
2019/10/24 Javascript
Python EOL while scanning string literal问题解决方法
2020/09/18 Python
python实现指定字符串补全空格的方法
2015/04/30 Python
Python实现的概率分布运算操作示例
2017/08/14 Python
解决Pandas to_json()中文乱码,转化为json数组的问题
2018/05/10 Python
Python爬虫:url中带字典列表参数的编码转换方法
2019/08/21 Python
python 并发下载器实现方法示例
2019/11/22 Python
基于pytorch 预训练的词向量用法详解
2020/01/06 Python
Django框架静态文件处理、中间件、上传文件操作实例详解
2020/02/29 Python
使用pygame编写Flappy bird小游戏
2020/03/14 Python
python如何实现递归转非递归
2021/02/25 Python
亚马逊中国官方网站:amazon.cn
2017/05/25 全球购物
Internet体系结构
2014/12/21 面试题
机械设计毕业生自荐信
2014/02/02 职场文书
《晏子使楚》教学反思
2014/02/08 职场文书
工伤事故赔偿协议书(标准)
2014/09/29 职场文书
投标邀请书范本
2015/02/02 职场文书
初任公务员培训心得体会
2016/01/08 职场文书
详解vue中v-for的key唯一性
2021/05/15 Vue.js