Python列表的内存管理问题

8
我正在创建一个元组,然后使用以下代码将其转换为列表:
y=("hello","the","world")
y=list(y)

Python是否只是将对象标记为可变,并通过标签y访问,还是创建每个对象的完整副本,将其添加到新列表结构中,然后删除原始不可变对象?

谢谢。


有第三种选择。将原始对象的引用复制一份。请更新您的问题,以允许除了您的两个假设之外的其他选项。 - S.Lott
3个回答

15

执行该行代码期间

y = list(y)
以下发生了以下情况:
1. 右侧被评估。这包括创建一个新的列表对象。该列表对象填充元组对象传递给构造函数的项。这些项不会被复制。相反,它们的引用计数增加,并且对这些项的引用被添加到新的列表对象中。
2. 新创建的列表对象被分配给左侧的名称(y)。这包括首先取消分配名称,这导致减少之前指向的元组对象y的引用计数器。由于没有对这个元组对象的其他引用,因此它被删除。最后,y被设置为指向新的列表对象。

10

通过检查每个对象的id,您可以找出它们的信息。

以下是我的运行结果。

y=("hello","the","world")
id(y), [id(i) for i in y]
(18627040, [21912480, 21964056, 21910304])

y = list(y)
id(y), [id(i) for i in y]
(21905536, [21912480, 21964056, 21910304])

如你所见,这些对象是相同的。

更新: Sven Marnach完美地解释了它的如何和为什么。仅供参考,我对其他类型的对象进行了更多的测试。

对于一个对象

class C: pass
x = (C(), C(), C())
id(x), [id(i) for i in x]
(18626400, [19992128, 19992008, 19991328])
x= list(x)
id(x), [id(i) for i in x]
(21863560, [19992128, 19992008, 19991328])

为了列举

z = ([], [], [])
id(z), [id(i) for i in z]
(18627040, [21908016, 21907136, 21908536])
z = list(z)
id(z), [id(i) for i in z]
(18614992, [21908016, 21907136, 21908536])

关于列表的列表

p = ([[], []], [[], []], [[], []])
id(p), [[id(i) for i in j] for j in p]
(18627040, [[21919504, 21895808], 
            [21894608, 21895008], 
            [19991008, 19789104]])
p = list(p)
id(p), [[id(i) for i in j] for j in p]
(19800352, [[21919504, 21895808], 
            [21894608, 21895008], 
            [19991008, 19789104]])

使用字符串来进行测试并不是一个好的方法。由于字符串是不可变的,在 CPython 中内容相同的字符串通常会被重用。 - ThiefMaster
@ThiefMaster 我对其他类型的对象进行了更多的测试。在所有情况下,对象的 id 都是相同的。我不确定我完全理解你的评论。你能否详细说明一下? - Praveen Gollakota

3

如果你没有明确要求,Python 不会进行深度复制。因此,结果将是一个新的、可变的列表,其中包含对元组中放置的对象的引用。

请注意,元组中的对象本身始终是可变的。只有字符串元组是不可变的,即你无法向元组添加/删除对象,但你始终可以访问和更改元组内部的对象。


1
它们不是因为字符串是不可变的。 - ThiefMaster
@ThiefMaster 你说得对,在这个特定的例子中它们并不是。但是在一般情况下,如果你的对象一开始就是可变的,那么如果你将它们存储在元组中,它们仍然是可变的。 - Wim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接