Python3多重赋值和内存地址

ist*_*ern 10 python python-3.x multiple-assignment

看完这个这个,这是非常相似,我的问题,我仍然无法理解以下行为:

a = 257
b = 257
print(a is b) #False
a, b = 257, 257
print(a is b) #True
Run Code Online (Sandbox Code Playgroud)

打印时id(a),id(b)我可以看到在单独的行中分配值的变量具有不同的ID,而对于多个赋值,两个值具有相同的id:

a = 257
b = 257
print(id(a)) #139828809414512
print(id(b)) #139828809414224
a, b = 257, 257
print(id(a)) #139828809414416
print(id(b)) #139828809414416
Run Code Online (Sandbox Code Playgroud)

但是,通过说多个相同值的赋值总是创建指向同一个id的指针,从而解释这种行为是不可能的:

a, b = -1000, -1000  
print(id(a)) #139828809414448
print(id(b)) #139828809414288
Run Code Online (Sandbox Code Playgroud)

是否有一个明确的规则,它解释了变量何时变得相同id而不是?

编辑

相关信息:此问题中的代码以交互模式运行(ipython3)

use*_*ica 2

这是由于字节码编译器中不断的折叠优化所致。当字节码编译器编译一批语句时,它使用字典来跟踪它所看到的常量。该字典自动合并任何等效的常量。

这是负责记录和编号常量的例程(以及一些相关的职责):

static int
compiler_add_o(struct compiler *c, PyObject *dict, PyObject *o)
{
    PyObject *t, *v;
    Py_ssize_t arg;

    t = _PyCode_ConstantKey(o);
    if (t == NULL)
        return -1;

    v = PyDict_GetItem(dict, t);
    if (!v) {
        arg = PyDict_Size(dict);
        v = PyInt_FromLong(arg);
        if (!v) {
            Py_DECREF(t);
            return -1;
        }
        if (PyDict_SetItem(dict, t, v) < 0) {
            Py_DECREF(t);
            Py_DECREF(v);
            return -1;
        }
        Py_DECREF(v);
    }
    else
        arg = PyInt_AsLong(v);
    Py_DECREF(t);
    return arg;
}
Run Code Online (Sandbox Code Playgroud)

您可以看到,如果没有找到已存在的等效常量,它只会添加一个新条目并分配一个新数字。(该_PyCode_ConstantKey位确保0.0-0.0、 和 等内容0被认为是不等价的。)

在交互模式下,每次解释器必须实际运行命令时,批处理就会结束,因此常量折叠通常不会在命令之间发生:

>>> a = 1000
>>> b = 1000
>>> a is b
False
>>> a = 1000; b = 1000 # 1 batch
>>> a is b
True
Run Code Online (Sandbox Code Playgroud)

在脚本中,所有顶级语句都是一批,因此会发生更多常量折叠

a = 257
b = 257
print a is b
Run Code Online (Sandbox Code Playgroud)

在脚本中,这会打印True.

函数的代码将其常量与函数外部的代码分开跟踪,这限制了常量折叠:

a = 257

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

f()
Run Code Online (Sandbox Code Playgroud)

即使在脚本中,也会打印此内容False