为什么以不同方式声明相同元素的元组会导致多个内存地址而不是一个?

问题描述 投票:0回答:1

我正在学习 python 中的内存管理。最近我正在探索可变对象和不可变对象的内存地址差异。起初我得出的结论是,出于优化目的,相同的对象也是不可变的,结果只有一个内存地址。可变对象总是接收新的内存地址。

但是在询问了一些人并探索了一些代码片段之后,我得到了我无法理解的结果。请看下面的代码:

x = (1, 2)  # 0x111
y = (1, 2)  # 0x111

print(hex(id(x)))
print(hex(id(y)))

a = tuple([1, 2])  # 0x222
b = tuple((1, 2))  # 0x111
c = tuple(range(1, 2))  # 0x333
d = tuple(_ for _ in x)  # 0x444

print(hex(id(a)))
print(hex(id(b)))
print(hex(id(c)))
print(hex(id(d)))

print(x, y, a, b, c, d)  # (1, 2) (1, 2) (1, 2) (1, 2) (1, 2) (1, 2)

根据这段代码,我希望它们都应该有相同的内存地址。

为什么那些相同的元组之间的内存地址不同?

编辑:我还应该声明我想在 IDE 中讨论这个过程,而不是 python 控制台。

python memory tuples cpython
1个回答
0
投票

当您调用

tuple([1, 2])
(或任何非空操作;毕竟,调用
tuple((1, 2))
只能返回相同的元组)时,Python 不会只查看它之前构造的所有元组,所以它可以返回同样的东西。 (如果你想要那样的东西,你会使用
@functools.cache
)。这就是为什么
tuple([1, 2])
和所有其他人返回具有新 ID 的新对象的原因。

此外,当您使用 Python(好吧,CPython,因为这是一个实现细节)加载模块(例如从磁盘)时,它会被编译成字节码,并且该过程确实包括一两个优化。

如果我们在您的代码上调用

dis
反汇编器,我们看到它以

开头
$ python3.10 -m dis so75512629.py
  1           0 LOAD_CONST               0 ((1, 2))
              2 STORE_NAME               0 (x)

  2           4 LOAD_CONST               0 ((1, 2))
              6 STORE_NAME               1 (y)

即同样的第0个const保存成两个不同的名字,后面就

  8          52 LOAD_NAME                5 (tuple)
             54 LOAD_CONST               0 ((1, 2))
             56 CALL_FUNCTION            1
             58 STORE_NAME               7 (b)

我们在同一个 const 上调用

tuple
(正如上面所讨论的,
tuple(t)
对于元组
t
是一个空操作)。

在 REPL 会话中进入单独的套件时,这 成立,因为它们是单独编译的:

>>> a = (1, 2)
>>> b = (1, 2)
>>> id(a)
4318599488
>>> id(b)
4319249472
>>>

但是,优化确实适用于within单个编译套件:

>>> c = [(1, 2), (1, 2)]
>>> id(c[0])
4319257472
>>> id(c[1])
4319257472
© www.soinside.com 2019 - 2024. All rights reserved.