引言
Python 有一个称为整数缓存的特殊功能。有时候,它可能会让我们感到困惑。例如,如果我们在 Python shell 上运行下面的代码,结果是令人惊讶的:
>>> a=256
>>> b=256
>>> a is b
True
>>> x=257
>>> y=257
>>> x is y
False
为什么256是256,而257不是257?有什么问题吗?
这还不是最奇怪的部分,如果我们把相同的代码放到一个文件中,并以 Python 脚本的形式运行它,结果会再次让我们大吃一惊!
a = 256
b = 256
print(a is b)
x = 257
y = 257
print(x is y)
我们将所有代码放入上面的脚本“ test.py”中,然后打开一个终端并运行脚本:
$ python test.py
True
True
如果你对这些也感到困惑,那就不用担心了。本文将揭示整数缓存特性的工作原理,并将其总结为三个事实。读完之后,你就不会再对它感到困惑了。
1. Python中的一小部分整数是Singleton
实际上,为了节省时间和内存开销,Python 总是预加载[-5,256]范围内的所有小整数。当在这个范围内声明一个新的整数变量时,Python 只是将缓存的整数引用到它,不会创建任何新的对象。
换句话说,[-5,256]中的所有整数都是Singletons,它们已经被保存在内存中了。我们不能在这个范围内创建新的整数对象。
因此,对第一个例子的解释是显而易见的:
· 当变量a及b 被分配到256上,它们被引用到存放256的同一个存储位置,他们指向同一个对象。
· 当变量a及b被分配给257,他们是两个不同的对象在不同的内存位置。
由于 is 操作符用于比较两个变量的内存位置,因此 a is b 应该输出 True,x is y 应该输出 False。
我们还可以使用 id ()函数,它打印出变量的内存位置,以证实上述解释:
>>> a=256
>>> b=256
>>> id(a)==id(b)
True
>>> x=257
>>> y=257
>>> id(x)==id(y)
False
2.如果python编译器可以看到全部,则情况就不一样了
如前所示,如果我们运行与 Python 脚本相同的代码,x = y 将输出 True。这当然不是偶然的。
事实上,Python 编译器是非常聪明的,并且会在引擎盖下为我们做许多优化。如果我们将代码作为一个整体运行,Python 编译器可以立即“看到”整个程序并进行相应的优化。
至于我们的例子,因为 Python 编译器看到了整个程序,它知道有两个变量 x 和 y,它们等于同一个整数257。因此,创建一个整数对象并将两个变量引用到其中是合理的。这种优化可以节省时间和内存成本。
但是,如果我们在交互式 Python shell 中逐行运行代码,编译器每次只能看到一行代码。
这意味着当我们输入 y = 257时,编译器只能看到这一行代码,而不能记住之前的代码 x = 257。因为257不在特殊范围内(即[-5,256]) ,所以会创建一个新的整数对象。
因为我们知道 Python 编译器的这个特性,所以我们可以使用一个技巧在交互式 shell 中使 x is y 输出为 True:
>>> x=257; y=257
>>> x is y
True
3. Python的实现和版本会影响整数缓存功能
上述所有示例和解释都基于 CPython,它是 Python 的标准实现。事实上,有一些替代的实现,比如 PyPy、 IronPython 和 Jython,它们的一些机制可能与 CPython 不同。
例如,在 PyPy 中,默认情况下禁用小整数缓存特性。如果我们想打开这个特性,我们应该首先启用它。有关详细信息,我们可以查看相应的 PyPy 文档。
此外,Python 的版本也很重要。我们在本文中使用的是 Python 3.8。由于 Python 是不断更新和发展的,没有人能够确保在未来的版本中不会改变一个特殊的功能。
总结
Python 内部有许多优化机制,我们应该了解它是如何工作的并影响我们的代码。整数缓存特性对于减少时间和内存开销是非常有用和必要的。
· END ·
HAPPY LIFE