在Python中,整数对象的复用涉及两个层面:
第一层是小整数池(small integers pool)1机制。Python会预先缓存一定范围内的整数对象(通常是[-5, 256]),这个范围内的整数总是会复用同一个对象。比如:
a = 256
b = 256
print(a is b) # True,因为256在小整数池范围内
第二层是Python解释器的编译优化。当Python将源代码编译成字节码时,如果发现同一个编译单元(compilation unit)中有相同的字面量,会尝试复用它们。但这种优化的行为会因执行环境而异:
-
在
.py
文件中,整个文件是一个编译单元:x = 257 y = 257 print(x is y) # 输出True,因为编译器对整个文件进行了优化
-
在REPL环境中,每一行输入都是独立的编译单元:
>>> x = 257 # 第一个编译单元 >>> y = 257 # 第二个编译单元 >>> x is y # 输出False,因为是在不同的编译单元中创建的对象
我们可以用
exec()
来模拟REPL的这种行为:namespace = {} exec("x = 257", namespace) # 相当于一个独立的编译单元 exec("y = 257", namespace) # 另一个独立的编译单元 exec("print(x is y)", namespace) # 输出False
这个行为提醒我们:
- 不要依赖Python的对象复用机制编写代码,因为它是实现细节,可能随版本变化
- 使用
is
运算符时要格外小心,它比较的是对象的身份(identity),而不是值 - 如果要比较值是否相等,应该使用
==
运算符:x = 257 y = 257 print(x == y) # 始终为True,因为比较的是值而不是身份
这里还有个有趣的实验可以帮助理解:
# 在同一行定义的变量会复用对象
>>> x, y = 257, 257
>>> x is y
True
# 在不同行定义的变量会创建新对象
>>> x = 257
>>> y = 257
>>> x is y
False
# 但如果使用同一个表达式,也会复用对象
>>> y = x = 257
>>> x is y
True
这进一步说明了Python解释器的优化行为:当它能够在编译时确定多个变量会被赋予相同的字面量值时,就会尝试复用对象。但如果是在REPL环境下分行输入,每行都是独立的编译单元,就不会发生这种优化。
短字符串同理 ↩︎