189 8069 5689

Python中小整数对象池和字符串intern的示例分析-创新互联

这篇文章将为大家详细讲解有关Python中小整数对象池和字符串intern的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

创新互联建站是一家专业提供桂平企业网站建设,专注与成都做网站、成都网站制作、H5网站设计、小程序制作等业务。10年已为桂平众多企业、政府机构等服务。创新互联专业的建站公司优惠进行中。

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 ()
10 LOAD_CONST 2 ('f')
12 MAKE_FUNCTION 0
14 STORE_NAME 2 (f)
16 LOAD_CONST 3 (None)
18 RETURN_VALUE

Disassembly of :
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的优点有哪些

1、简单易用,与C/C++、Java、C# 等传统语言相比,Python对代码格式的要求没有那么严格;2、Python属于开源的,所有人都可以看到源代码,并且可以被移植在许多平台上使用;3、Python面向对象,能够支持面向过程编程,也支持面向对象编程;4、Python是一种解释性语言,Python写的程序不需要编译成二进制代码,可以直接从源代码运行程序;5、Python功能强大,拥有的模块众多,基本能够实现所有的常见功能。

关于“Python中小整数对象池和字符串intern的示例分析”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。


分享文章:Python中小整数对象池和字符串intern的示例分析-创新互联
当前地址:http://jkwzsj.com/article/cciosh.html