我对以下情况感到困惑,也许我的词汇在这里是错误的,所以很抱歉。
假设我们有一个元组,x = ('a', [])
然后我们做x = (x[0], [1, 2, 3])
.
由于新元组引用了旧元组,我们不能删除旧元组对象,但由于旧元组对象我们只使用对 x[0] 的引用,所以我们正在浪费内存作为旧元组的 x[1]元组对象不能被任何东西访问。
这是内存泄漏的真实案例吗?我们是否只是为了让新元组对象的引用有意义而浪费内存。
我对以下情况感到困惑,也许我的词汇在这里是错误的,所以很抱歉。
假设我们有一个元组,x = ('a', [])
然后我们做x = (x[0], [1, 2, 3])
.
由于新元组引用了旧元组,我们不能删除旧元组对象,但由于旧元组对象我们只使用对 x[0] 的引用,所以我们正在浪费内存作为旧元组的 x[1]元组对象不能被任何东西访问。
这是内存泄漏的真实案例吗?我们是否只是为了让新元组对象的引用有意义而浪费内存。
您可能是从像 C++ 这样的语言开始使用 Python 的,其中变量是存储值的内存位置。在 Python 中,值存储在您不必担心的地方,而您的变量只是引用这些值的名称价值观。没有办法让一个变量引用另一个变量*——您可以让它引用与另一个变量相同的值,但这不是问题。
例如,在 C++ 中:
int x[] = {1, 2, 3};
int &y = x[0];
这里,x
是一个足以容纳三个int
值的内存位置,并且y
是对这些位置中的第一个的引用。所以,如果在还活着的时候x
就消失了y
,你会有一个悬空的参考,这会很糟糕。但在 Python 中:
x = [1, 2, 3]
y = x[0]
这里,x
是一个list
对象的名称,它的三个位置是int
存储在别处的三个不同对象的名称,并且y
只是第一个int
对象的另一个名称。如果x
消失,Python 可以释放list
, 之后它可以释放2
and3
对象**(因为没有其他人引用它们),只留下1
对象。
这也是 Python 中没有“复制构造函数”*** 的原因。在 C++ 中,int z = x[0]
创建一个新的内存位置,并将int
from隐式复制x[0]
到该内存位置;在 Python 中,除非你明确地编写类似的东西,否则你z = copy.copy(x[0])
永远不会复制任何东西。
* 如果您查看封闭单元如何在幕后工作,这并不完全正确。
** 事实上,小整数通常会被特殊对待,并且永远保持活动状态,但让我们忽略这一点。
*** 少得多的复制赋值运算符、移动构造函数和移动赋值运算符。
这是一个带注释的 Python 会话,使用您的示例来显示当前引用和未引用的内容。
>>> x = ('a', [])
>>> id(x)
4340112344 # The tuple
>>> id(x[0])
4339406128 # The character 'a'
>>> id(x[1])
4340109184 # An empty list
>>> x = (x[0], [1,2,3])
>>> id(x)
4340112488 # x refers to a new object
>>> id(x[0])
4339406128 # The character 'a'. The old tuple is not involved
>>> id(x[1])
4340199576 # A new list.
原始元组对象 4340112344 不再被引用,因此可以在方便时自由地进行垃圾回收,而不会影响x
.
轻微的逻辑错误。
Python 字符串是不可变的。当你这样做时:
x = ('a', [])
然后这个:
x = (x[0], [1,2,3])
x[0]
现在是一个新字符串,即不绑定到原来的字符串。因此 GC 可以自由地收集原始元组。
以上无效!!!哎呀!!!!!!!逻辑错误!!!下面的文字对所有类型都有效!!!
但是,如果我们这样做:
x = (x[1], 'Hi!')
原始元组仍然被删除,即使x[1]
是对列表的引用。为什么?因为列表没有绑定到元组。两者是独立的对象。因此,当您引用 时x[1]
,可以安全地删除其他元素以及原始元组本身。
底线:如果一个对象是活着的,那并不意味着它的父对象是活着的。如果我们继续覆盖x
,它每次都会被删除,除了一个需要保持活动的对象。
所以,没有内存泄漏。