gpt4 book ai didi

python - 缓存整数,Python 3.7 中的 `is` 运算符和 `id()`

转载 作者:太空狗 更新时间:2023-10-29 21:35:54 24 4
gpt4 key购买 nike

在我的 Python 演讲中,当谈论一些 Python 琐事时,我曾经展示过类似 print(5 is 7 - 2, 300 is 302 - 2) 的内容。今天我意识到这个例子在 Python 3.7 中运行时产生了(对我来说)意想不到的结果。

我们知道从-5到255的数字在内部缓存Python 3 docs - PyLong_FromLong也可以在早期的 API 文档中找到。

is 运算符(如文档 Python 3 docs - is operator 中所述)测试对象身份,即它使用 id() 函数来确定并产生 True 当值匹配时。

id() 函数保证在对象的生命周期内返回一个唯一且常量的值(在文档 Python 3 docs - id() 中也有描述)。

所有这些规则都会为您提供以下结果(正如许多 Python 编码人员所知):

python 2.7:

>>> print(5 is 7 - 2, 300 is 302 - 2)
True False

python 3.6:

>>> print(5 is 7 - 2, 300 is 302 - 2)
True False

但是,Python 3.7 的行为有所不同:

>>> print(5 is 7 - 2, 300 is 302 - 2)
True True

我试图理解原因,但我在 Python 源代码中找不到任何提示...

id(302 - 2) 总是产生不同的值,所以我想知道为什么 302 - 2 is 300 产生 Trueis 运算符如何知道值相同?对于 Python 3.7 中的整数比较,这是否以某种方式重载?

>>> id(300)
140059023515344

>>> id(302 - 2)
140059037091600

>>> id(300) is id(302 - 2)
False

>>> 300 is 302 - 2
True

>>> id(300) == id(302 -2)
True

>>> id(302 - 2)
140059037090320

>>> id(302 - 2)
140059023514640

最佳答案

没有改变。语言语义的任何部分都没有改变;您正在比较的对象是否是同一对象从未指定行为。 is 比较的两侧现在恰好是同一个对象。这是常量折叠优化变化的结果。

代码对象的 co_consts 的初始生成为等效的原子常量重用单个对象。 (我说“等价”而不是“等于”,因为 1 和 1.0 不等价。)这与缓存从 -5 到 256 的整数的效果不同,它只适用于单个代码对象。 Previously ,将 302 - 2 转换为 300 的编译时优化过程发生在字节码窥孔优化器中,它在初始 co_consts 生成后启动,并且不会进行相同的持续重用。

在 CPython 3.7 中,这个优化过程是 moved从字节码窥孔优化器到新的 AST 优化器。 AST 优化器在初始生成代码对象的 co_consts 之前生效,因此常量重用现在适用于结果。


您可以通过执行类似的操作来查看持续重用对旧 Python 版本的影响

>>> 300 is 300
True

即使在 CPython 2.7 或 3.6 上也会产生 True,尽管 300 超出了小整数缓存的范围。您可以通过确保您正在比较的常量最终出现在单独的代码对象中来防止常量重用:

>>> (lambda: 300)() is 300
False

这在任何版本的 CPython 上都会产生 False,即使新的优化器发生了变化。但是,它在 PyPy 上生成 True,因为 PyPy 有自己的优化行为,并且 PyPy 的行为就好像所有相等的整数都由相同的整数对象表示一样。

关于python - 缓存整数,Python 3.7 中的 `is` 运算符和 `id()`,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55483900/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com