>>> a=1 >>> b=1 >>> id(a) 140472563599848 >>> id(b) 140472563599848 >>> x=() >>> y=() >>> id(x) 4298207312 >>> id(y) 4298207312 >>> x1=(1) >>> x2=(1) >>> id(x1) 140472563599848 >>> id(x2) 140472563599848
直到此时我还在想只会有一个不可变对象的副本,并且它将被所有变量共享(指向)。
但是当我尝试以下步骤时我明白我错了。
>>> x1=(1,5) >>> y1=(1,5) >>> id(x1) 4299267248 >>> id(y1) 4299267320
有人可以向我解释一下其内部原理吗?
>>> x1=(1) >>> x2=(1)
实际上与
>>> x1=1 >>> x2=1
在 Python 中,较小的数字会被内部缓存。因此它们不会在内存中多次创建。这就是为什么id和x1的x2到目前为止都是相同的。
id
x1
x2
单元素元组末尾应该有一个逗号,如下所示
>>> x1=(1,) >>> x2=(1,)
执行此操作时,将构造两个新的元组,每个元组中只有一个元素。即使元组中的元素相同,它们也是不同的元组。这就是为什么它们都有不同的ids。
让我们采取你的最后一个例子并反汇编代码。
compiled_code = compile("x1 = (1, 5); y1 = (1, 5)", "string", "exec")
现在,
import dis dis.dis(compiled_code)
会产生类似这样的结果
1 0 LOAD_CONST 3 ((1, 5)) 3 STORE_NAME 0 (x1) 6 LOAD_CONST 4 ((1, 5)) 9 STORE_NAME 1 (y1) 12 LOAD_CONST 2 (None) 15 RETURN_VALUE
它加载索引为 的常量值,3即(1, 5),然后将其存储在 中x1。同样,它加载索引处的另一个常量值,4并将其存储在 中y1。如果我们查看代码对象中的常量列表,
3
(1, 5)
4
y1
print(compiled_code.co_consts)
将给予
(1, 5, None, (1, 5), (1, 5))
3位置和处的元素4是我们在实际代码中创建的元组。因此,Python 不会为每个不可变对象只创建一个实例,始终如此。这是一个实现细节,我们不必太担心。
注意:如果你希望只有一个不可变对象的实例,你可以手动这样做
x1 = (1, 5) x2 = x1
现在,x2和都x1将引用同一个元组对象。
我想说的是,这种行为实际上已经发生了变化(从 Python 3.9 开始,尽管我不确定具体在哪个版本发生了变化)。现在看来,Python 编译器试图重用“一起编译”的代码的元组(不确定它是如何工作的),因此如果您x = (1, 5); y = (1, 5)在交互式 shell 中输入(一行),x is y则实际上为真。但是,如果您在交互式 shell 中输入x = (1, 5)和y = (1, 5)两行,那么它们仍然会有不同的 ID(与小整数和字符串不同)。 –
在我的 3.10 中,compiled_code.co_consts是((1, 5), None)- 元组被驻留。事实上,如果你在 shell 中的函数中或 .py 文件中的任何地方执行此操作x1=(1,),x2=(1,)它将被驻留为单个元组。我认为这只是强调了这只是一个实现细节,并且可以随时更改