从Python2到Python3的解包行为发生了什么变化

2024-10-04 13:17:16 发布

您现在位置:Python中文网/ 问答频道 /正文

昨天,我在python2和python3之间发现了一个奇怪的解包差异,在Google上快速搜索之后似乎没有找到任何解释。在

Python 2.7.8

a = 257
b = 257
a is b # False

a, b = 257, 257
a is b # False

Python3.4.2

^{pr2}$

我知道它可能不会影响程序的正确性,但它确实让我有点烦。有人能给我们一些关于拆箱的区别的见解吗?在


Tags: 程序falseisgoogle差异python3python2区别
2条回答

这种行为至少在一定程度上与解释器如何进行常量折叠有关 以及REPL如何执行代码。在

首先,请记住CPython首先编译代码(到AST,然后编译字节码)。然后评估 字节码。在编译期间,脚本查找不可变的对象并将其缓存。它 还可以对它们进行重复数据消除。所以如果它看到

a = 257
b = 257

它将针对同一对象存储a和b:

^{pr2}$

注意LOAD_CONST 11co_consts的索引:

f.__code__.co_consts
#>>> (None, 257)

所以这两个加载相同的257。为什么不出现这种情况:

$ python2
Python 2.7.8 (default, Sep 24 2014, 18:26:21) 
>>> a = 257
>>> b = 257
>>> a is b
False

$ python3
Python 3.4.2 (default, Oct  8 2014, 13:44:52) 
>>> a = 257
>>> b = 257
>>> a is b
False

是吗?在

本例中的每一行都是一个单独的编译单元,并且 重复数据消除无法在它们之间进行。它的工作原理与

compile a = 257
run     a = 257
compile b = 257
run     b = 257
compile a is b
run     a is b

因此,这些代码对象都将具有唯一的常量缓存。 这意味着如果删除换行符,is将返回True

>>> a = 257; b = 257
>>> a is b
True

实际上,这两个Python版本都是如此。事实上,这就是为什么

>>> a, b = 257, 257
>>> a is b
True

也返回True;这不是因为解包的任何属性;它们 放在同一个编译单元。在

对于没有正确折叠的版本,返回Falsefilmor links to Ideone,这表明在2.7.3和3.2.3上失败。在这些版本中,创建的元组不会与其他常量共享它们的项:

import dis

def f():
    a, b = 257, 257
    print(a is b)

print(f.__code__.co_consts)
#>>> (None, 257, (257, 257))

n = f.__code__.co_consts[1]
n1 = f.__code__.co_consts[2][0]
n2 = f.__code__.co_consts[2][1]

print(id(n), id(n1), id(n2))
#>>> (148384292, 148384304, 148384496)

不过,这并不是关于对象解包方式的改变,而是对象在co_consts中的存储方式的改变。在

我认为这实际上是偶然的,因为我无法用Python3.2重现这种行为。在

这个问题http://bugs.python.org/issue11244引入了一个CONST_STACK来解决带有负数的常量元组没有被优化的问题(看看针对peephole.c的补丁,其中包含Python的优化程序运行)。在

这似乎也导致了既定的行为。还在调查中:)

相关问题 更多 >