结构化分配和“正常”分配之间有什么区别?

时间:2017-12-24 04:48:35

标签: python

我在python 2.7.6 REPL中玩弄并遇到了这种行为。

>>> x = -10
>>> y = -10
>>> x is y
False
>>> x, y = [-10, -10]
>>> x is y
True

似乎destructured赋值为等效值返回相同的引用。这是为什么?

2 个答案:

答案 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 = -10y = -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等)可以不同地再次处理常量。