本文为is同一性运算符的详细解释。is用于判断两个对象是否为同一个对象,具体来说是两个对象在内存中的位置是否相同。

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

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

小整数对象池

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

  1. >>> i = -
  2. >>> j = -
  3. >>> i is j # i和j是同一个对象
  4. True
  5. >>> i =
  6. >>> j =
  7. >>> i is j # i和j是同一个对象
  8. True
  9. >>> i =
  10. >>> j =
  11. >>> i is j # i和j是不同对象
  12. False

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

字符串intern

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

  1. >>> i = ""
  2. >>> j = ""
  3. >>> i is j
  4. True

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

  1. >>> i = "1 2"
  2. >>> j = "1 2"
  3. >>> i is j
  4. False
  1. i = "1 2"
  2. j = "1 2"
  3. print(i is j)

输出结果

  1. True

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

编译机制

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

  1. i = "1 2"
  2. j = "1 2"
  3. print(i is j)
  4.  
  5. def f():
  6. pass

编译结果

  1. 2 0 LOAD_CONST 0 ('1 2')
  2. 2 STORE_NAME 0 (i)
  3.  
  4. 3 4 LOAD_CONST 0 ('1 2')
  5. 6 STORE_NAME 1 (j)
  6.  
  7. 5 8 LOAD_CONST 1 (<code object f at 0x00000200F257CF60, file "small_int.py", line 5>)
  8. 10 LOAD_CONST 2 ('f')
  9. 12 MAKE_FUNCTION 0
  10. 14 STORE_NAME 2 (f)
  11. 16 LOAD_CONST 3 (None)
  12. 18 RETURN_VALUE
  13.  
  14. Disassembly of <code object f at 0x00000200F257CF60, file "small_int.py", line 5>:
  15. 6 0 LOAD_CONST 0 (None)
  16. 2 RETURN_VALUE

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

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

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

  1. a = ""
  2. b = ""
  3. c = "1 2"
  4. d = "1 2"
  5. e = 257
  6. f = 257
  7. g = 2424234234234234
  8. h = 2424234234234234
  9. print(a is b, c is d, e is f, g is h)

输出结果

  1. True True True True

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

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

  1. i = 257
  2. j = 257
  3. a = i - 1
  4. b = i - 1
  5. c = i + 1
  6. d = i + 1
  7. print(i is j, a is b, c is d)

输出结果

  1. 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,但如果是单个字符,就会使用到字符缓冲池。

  1. k = "bbb"
  2. a = k[0]
  3. b = k[0]
  4. c = k[1:]
  5. d = k[1:]
  6. print(a, d)
  7. print(a is b, c is d)

输出结果

  1. b bb
  2. True False

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

编译机制与字符串intern对比

  1. i = "1 2"
  2. j = ""
  3. k = "__fjdslfjaskfas"
  4.  
  5. ii = "1 2"
  6. jj = ""
  7. kk = "__fjdslfjaskfas"
  8.  
  9. def f():
  10. a = "1 2"
  11. b = ""
  12. c = "__fjdslfjaskfas"
  13. return a is i, b is j, c is k
  14.  
  15. print("Code:", i is ii, j is jj, k is kk)
  16. print(f"intern: {f()}")

输出结果

  1. Code: True True True
  2. 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 tips:小整数对象池与字符串intern的更多相关文章

  1. Python中小整数对象池和大整数对象池

    1. 小整数对象池 整数在程序中的使用非常广泛,Python为了优化速度,使用了小整数对象池, 避免为整数频繁申请和销毁内存空间. Python 对小整数的定义是 [-5, 256] 这些整数对象是提 ...

  2. python中小整数对象池及intern机制

    小整数对象池: Python为了优化速度,使用了小整数对象池,避免为整数频繁申请和销毁 Python 对小整数的定义是 [-5, 256] 这些整数对象是提前建立好的,不会被垃圾回收,所有位于这个范围 ...

  3. [python 源码]整数对象的创建和维护

    刚开始学python时候,发现一个很迷惑的现象,一直到看了源码后才知道了: >>> a=6 >>> b=6 >>> a is b True 想用同 ...

  4. Python如何将整数转化成二进制字符串

    Python 如何将整数转化成二进制字符串 1.你可以自己写函数采用 %2 的方式来算. >>> binary = lambda n: '' if n==0 else binary( ...

  5. Python的小整数池

    此处经常会作为面试题!!! 小整数池目的:节省内存,提高执行效率 需要注意的是:Python实现int的时候有个小整数池.为了避免因创建相同的值而重复申请内存空间所带来的效率问题, Python解释器 ...

  6. python 变量之小整数池跟大整数池

    在python中定义变量会有:id,type,value.对于==比较的是value,对于is比较的是id. 因此,对于相同value的变量,它的type相同,但是它的id值可能不一样.对于相同id的 ...

  7. python 小整数池 和intern 【整理】

    小整数对象池 (在python内置了) 整数在程序中的使用非常广泛,Python为了优化速度,使用了小整数对象池,避免为整数频繁申请和销毁内存空间. Python对小整数的定义是[-5,257]这些整 ...

  8. Python 源码剖析(二)【整数对象】

    二.整数对象 1.PyIntObject 2.PyIntObject 对象的创建和维护 3.Hack PyIntObject 1.PyIntObject PyIntObject的定义: [intobj ...

  9. 《python解释器源码剖析》第6章--python中的dict对象

    6.0 序 元素和元素之间可能存在着某种关系,比如学生姓名和成绩.我希望能够通过学生的姓名找到这个学生的成绩,那么只需要将两者关联起来即可.字典正是这么做的,字典中的每个元素就是一个key:value ...

随机推荐

  1. App架构设计经验谈:接口”安全机制”的设计

    [原文地址 点击打开链接] 原创文章,转载请注明:转载自Keegan小钢 并标明原文链接:http://keeganlee.me/post/architecture/20160107 微信订阅号:ke ...

  2. [Cogs728] [网络流24题#3] 最小路径覆盖 [网络流,最大流,二分图匹配]

    建图:源点—>边的起点(集合1中的)—>边的终点(集合2中的)—>汇点,所有边权均为1, 计算最大流,最后枚举起点的出边,边权为0的即为匹配上的, 可以这样理解:每条边表示起点和终点 ...

  3. [cf 599C] Day at the Beach

    题意:有n个数,将其分组使整个数列排序后每组中的数仍在该组中,求最多的分组数. 代码很易懂 #include <iostream> #include <algorithm> # ...

  4. [ACM] POJ 3295 Tautology (构造)

    Tautology Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 9302   Accepted: 3549 Descrip ...

  5. Apache + Tomcat + JK 集群

    原文请见http://www.cnblogs.com/dennisit/p/3370220.html 本文介绍了集群和负载均衡的基本开源实现,实现了用Apache分发请求到多个Tomcat里面相应的应 ...

  6. codeforces 437D The Child and Zoo

    time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standa ...

  7. ListView点击或选中item改变背景

    点击或选中ListView中的一项后.使item背景改变,失去焦点相同显示选中的背景.又一次选中另外一项才刷新: 在Adapter中配置: public class MyAdapter extends ...

  8. Windowns 无法启动 Office Software Protection Platform 服务,系统找不到指定的文件

    导致该服务无法启动的原因是,用kms8激活了win7后又用oem8激活试了下,结果就这样,然后就无法激活了,状态ID都不可用.试过禁用计划任务项目,重建MBR,重建PBR,都无效果.最后在这里找到了解 ...

  9. 更改printk打印级别【转】

    本文转载自:http://blog.csdn.net/weed_hz/article/details/8949140 1.查看当前控制台的打印级别 cat /proc/sys/kernel/print ...

  10. bzoj 3231 [ Sdoi 2008 ] 递归数列 —— 矩阵乘法

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3231 裸矩阵乘法. 代码如下: #include<iostream> #incl ...