未在 Python 脚本中调用析构函数
Destructor not being called in a Python script
下面是按我预期执行的模块。
class Z():
def Y(self):
return
def __del__(self):
print('Z deleted.')
def W(v):
class Form:
def X(self):
#v.Y()
return
return
def U():
t = Z()
W(t)
U()
运行 上述模块产生以下输出
Z deleted.
当我如下所示删除注释时,没有产生任何输出。
class Z():
def Y(self):
return
def __del__(self):
print('Z deleted.')
def W(v):
class Form:
def X(self):
v.Y()
return
return
def U():
t = Z()
W(t)
U()
为什么不调用析构函数?
我运行这个模块在下面的实用程序中。操作系统是 Windows 10 Pro,版本 1803,OS build 17134.165
您编写的脚本正在以不太明显的方式创建引用循环。非显而易见的循环是 all class
声明本质上循环的结果,因此 W
中 class
声明的简单存在意味着那里将是一些循环垃圾。我不确定这是否是所有 Python 解释器的必要条件,但 CPython 的实现(至少从 2.7 到 3.6,我检查过的解释器)确实如此。
在你的 Z
实例中循环并触发你观察到的行为的事情是你使用 v
(这是对 Z
实例的引用)闭包范围当您将 Form.x
声明为 class
声明的一部分。闭包范围意味着只要调用 W
定义的 class Form
存在,关闭变量 v
(最终是 Z
的一个实例)将保持活动状态.
当您 运行 一个处于空闲状态的模块时,它会 运行 执行该模块的代码后将您转储到交互式提示,但 Python 是仍然 运行ning,因此它不会立即执行任何全局清理或 运行 循环 GC。 Z
的实例将 最终 被清理(至少在 CPython 3.4+ 上),但循环 GC 通常 运行 只有在相当长的一段时间后没有匹配释放的分配数量(我的解释器默认为 700,尽管这是一个实现细节)。但是该收集可能需要任意长的时间(在解释器退出之前执行了最终循环清理,但除此之外,没有任何保证)。
通过注释掉引用 v
的行,您不再关闭 v
,因此循环 class
不再使 v
保持活动状态,并且当最后一个引用消失时,v
被及时清理(在 CPython 的引用计数解释器上;在 Jython、PyPy、IronPython 等上没有保证)。
如果你想强制清理,在 运行 模块之后,你可以 运行 在生成的交互 shell 中使用以下命令强制清理第 0 代:
>>> import gc
>>> gc.collect(0) # Or just gc.collect() for a full cycle collection of all generations
或者只需将相同的行添加到脚本末尾即可自动触发它。
下面是按我预期执行的模块。
class Z():
def Y(self):
return
def __del__(self):
print('Z deleted.')
def W(v):
class Form:
def X(self):
#v.Y()
return
return
def U():
t = Z()
W(t)
U()
运行 上述模块产生以下输出
Z deleted.
当我如下所示删除注释时,没有产生任何输出。
class Z():
def Y(self):
return
def __del__(self):
print('Z deleted.')
def W(v):
class Form:
def X(self):
v.Y()
return
return
def U():
t = Z()
W(t)
U()
为什么不调用析构函数?
我运行这个模块在下面的实用程序中。操作系统是 Windows 10 Pro,版本 1803,OS build 17134.165
您编写的脚本正在以不太明显的方式创建引用循环。非显而易见的循环是 all class
声明本质上循环的结果,因此 W
中 class
声明的简单存在意味着那里将是一些循环垃圾。我不确定这是否是所有 Python 解释器的必要条件,但 CPython 的实现(至少从 2.7 到 3.6,我检查过的解释器)确实如此。
在你的 Z
实例中循环并触发你观察到的行为的事情是你使用 v
(这是对 Z
实例的引用)闭包范围当您将 Form.x
声明为 class
声明的一部分。闭包范围意味着只要调用 W
定义的 class Form
存在,关闭变量 v
(最终是 Z
的一个实例)将保持活动状态.
当您 运行 一个处于空闲状态的模块时,它会 运行 执行该模块的代码后将您转储到交互式提示,但 Python 是仍然 运行ning,因此它不会立即执行任何全局清理或 运行 循环 GC。 Z
的实例将 最终 被清理(至少在 CPython 3.4+ 上),但循环 GC 通常 运行 只有在相当长的一段时间后没有匹配释放的分配数量(我的解释器默认为 700,尽管这是一个实现细节)。但是该收集可能需要任意长的时间(在解释器退出之前执行了最终循环清理,但除此之外,没有任何保证)。
通过注释掉引用 v
的行,您不再关闭 v
,因此循环 class
不再使 v
保持活动状态,并且当最后一个引用消失时,v
被及时清理(在 CPython 的引用计数解释器上;在 Jython、PyPy、IronPython 等上没有保证)。
如果你想强制清理,在 运行 模块之后,你可以 运行 在生成的交互 shell 中使用以下命令强制清理第 0 代:
>>> import gc
>>> gc.collect(0) # Or just gc.collect() for a full cycle collection of all generations
或者只需将相同的行添加到脚本末尾即可自动触发它。