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生成日历实例解析
Aug 21 Python
使用Pyrex来扩展和加速Python程序的教程
Apr 13 Python
在Python操作时间和日期之asctime()方法的使用
May 22 Python
给你选择Python语言实现机器学习算法的三大理由
Nov 15 Python
Python 3.3实现计算两个日期间隔秒数/天数的方法示例
Jan 07 Python
django-初始配置(纯手写)详解
Jul 30 Python
使用PyOpenGL绘制三维坐标系实例
Dec 24 Python
Python3 虚拟开发环境搭建过程(图文详解)
Jan 06 Python
Python3操作读写CSV文件使用包过程解析
Apr 10 Python
有关pycharm登录github时有的时候会报错connection reset的问题
Sep 15 Python
python 实现百度网盘非会员上传超过500个文件的方法
Jan 07 Python
Python 里最强的地图绘制神器
Mar 01 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的异常处理类Exception的使用及说明
2012/06/13 PHP
php批量上传的实现代码
2013/06/09 PHP
php操作redis缓存方法分享
2015/06/03 PHP
使用正则去除php代码中的注释方法
2016/11/03 PHP
网页图片延时加载的js代码
2010/04/22 Javascript
JS实现标签页效果(配合css)
2013/04/03 Javascript
js控制表单奇偶行样式的简单方法
2013/07/31 Javascript
js 一个关于图片onload加载的事
2013/11/10 Javascript
JQuery伸缩导航练习示例
2013/11/13 Javascript
js获取UserControl内容为拼html时提供方便
2014/11/02 Javascript
javascript判断元素存在和判断元素存在于实时的dom中的方法
2017/01/17 Javascript
easyui 中的datagrid跨页勾选问题的实现方法
2017/01/18 Javascript
在vue中实现简单页面逆传值的方法
2017/11/27 Javascript
JS实现判断图片是否加载完成的方法分析
2018/07/31 Javascript
如何利用node.js开发一个生成逐帧动画的小工具
2019/12/01 Javascript
[01:22:10]Ti4 循环赛第二日 DK vs Empire
2014/07/11 DOTA
python中精确输出JSON浮点数的方法
2014/04/18 Python
深入解析Python中的urllib2模块
2015/11/13 Python
Python正则表达式使用经典实例
2016/06/21 Python
django模型层(model)进行建表、查询与删除的基础教程
2017/11/21 Python
Python3网络爬虫之使用User Agent和代理IP隐藏身份
2017/11/23 Python
python在线编译器的简单原理及简单实现代码
2018/02/02 Python
对python程序内存泄漏调试的记录
2018/06/11 Python
python3的UnicodeDecodeError解决方法
2019/12/20 Python
python datetime处理时间小结
2020/04/16 Python
python退出循环的方法
2020/06/18 Python
CSS3 分类菜单效果
2019/05/27 HTML / CSS
html5中地理位置定位api接口开发应用小结
2013/01/04 HTML / CSS
韩国爱茉莉太平洋化妆品美国站:Amore Pacific US
2016/10/28 全球购物
国际礼品店:GiftsnIdeas
2018/05/03 全球购物
美国环保妈妈、儿童和婴儿用品购物网站:The Tot
2019/11/24 全球购物
五年后的职业生涯规划
2014/03/04 职场文书
实习证明格式范文
2014/10/14 职场文书
2014年行政后勤工作总结
2014/12/06 职场文书
评奖评优个人先进事迹材料
2015/11/04 职场文书
干货分享:推荐信写作技巧!
2019/06/21 职场文书