我在python 2.7.6 REPL中玩弄并遇到了这种行为。
>>> x = -10
>>> y = -10
>>> x is y
False
>>> x, y = [-10, -10]
>>> x is y
True
似乎destructured赋值为等效值返回相同的引用。这是为什么?
答案 0 :(得分:8)
我对Python一无所知,但我很好奇。
首先,在分配数组时也会发生这种情况:
x = [-10,-10]
x[0] is x[1] # True
它也会发生在不可变的字符串中。
x = ['foo', 'foo']
x[0] is x[1] # True
反汇编第一个函数:
0 LOAD_CONST 1 (-10)
3 LOAD_CONST 1 (-10)
6 BUILD_LIST 2
9 STORE_FAST 0 (x)
LOAD_CONST (consti)
op将常量co_consts[consti]
推入堆栈。但是这两个操作都有consti=1
,因此同一个对象被推送到堆栈两次。如果数组中的数字不同,它将反汇编为:
0 LOAD_CONST 1 (-10)
3 LOAD_CONST 2 (-20)
6 BUILD_LIST 2
9 STORE_FAST 0 (x)
这里,索引1和2的常量被推送。
co_consts
是Python脚本使用的常量元组。显然,具有相同值的文字只存储一次。
至于为什么'正常'赋值有效 - 你正在使用REPL所以我假设每行都是单独编译的。如果你把
x = -10
y = -10
print(x is y)
进入测试脚本,您将获得True
。因此,正常的分配和结构化分配在这方面都是相同的:)
答案 1 :(得分:6)
交互式Python解释器会分别编译每个语句。编译不仅产生字节码,而且还为任何内置的不可变类型(包括整数)生成常量。这些常量与代码对象一起存储为co_consts
属性。
您的x = -10
与y = -10
作业分开编译,最终得到完全独立的co_consts
结构。另一方面,x, y = [-10, -10]
可迭代赋值是一个赋值语句,一次传递给编译器,因此编译器可以重用常量。
您可以将简单语句(如赋值)放在一行中,并在它们之间加一个分号,此时,在Python 2.7中,您再次获得相同的-10
对象:
>>> x = -10; y = -10
>>> x is y
True
这里我们再次编译了一个语句,因此编译器可以决定它只需要一个对象来表示-10
值:
>>> compile('x = -10; y = -10', '', 'single').co_consts
(-10, None)
'single'
是交互式解释器使用的编译模式。编译后的字节码从这些常量中加载-10
值。
如果你把所有东西都放在一个函数中,你会得到同样的东西,编译成一个复合语句:
>>> def foo():
... x = -10
... y = -10
... return x is y
...
>>> foo()
True
>>> foo.__code__.co_consts
(None, -10)
模块也在一次传递中编译,因此模块中的全局变量可以共享常量。
所有这些都是实施细节。您应该从不,依靠此。
例如,在Python 3.6中,一元减号运算符是单独处理的(而不是-10
被视为单个整数文字),并且在窥视孔期间经常折叠后达到-10
值优化。这可以让您获得两个单独的-10
值:
>>> import sys
>>> sys.version_info
sys.version_info(major=3, minor=6, micro=3, releaselevel='final', serial=0)
>>> compile('x = -10; y = -10', '', 'single').co_consts
(10, None, -10, -10)
其他Python实现(PyPy,Jython,IronPython等)可以不同地再次处理常量。