CPython有一种奇怪的行为,它在关机期间将模块设置为None。这会在关闭我编写的一些多线程代码时搞乱错误记录。
我找不到此行为的任何文档。它在PEP 432中提到:
[...]显着减少将遇到“模块全局设置为无”行为的模块数量,该行为用于审议中断周期并尝试干净地释放更多外部资源。
有SO questions about this behaviour和C API documentation mentions shutdown behaviour for embedded interpreters。
我还找到了related thread on python-dev和related CPython bug:
此修补程序不会更改模块的行为 对象尽快清除它们的全局字典 他们被解除分配。
这种行为记录在哪里?是Python 2具体吗?
答案 0 :(得分:59)
作为此更改的一部分,在大多数情况下,在解释器关闭期间,模块全局变量不再被强制设置为
None
,而是依赖于循环垃圾收集器的正常操作。
行为的唯一文档是Python 3.4:
/* To make the execution order of destructors for global
objects a bit more predictable, we first zap all objects
whose name starts with a single underscore, before we clear
the entire dictionary. We zap them by replacing them with
None, rather than deleting them from the dictionary, to
avoid rehashing the dictionary (to some extent). */
请注意,将值设置为None
是一种优化;替代方法是从映射中删除名称,这会在尝试使用NameError
处理程序中的全局变量时导致不同的错误(AttributeError
异常而不是__del__
。
正如您在邮件列表中发现的那样,行为早于循环垃圾收集器;它是moduleobject.c
source code,而循环垃圾收集器是added in 1998。由于函数对象总是引用模块__dict__
,模块中的所有函数对象都涉及循环引用,这就是为什么__dict__
需要在GC之前清除的原因。
即使添加了循环GC,它仍然保留在原位,因为可能存在循环中涉及__del__
方法的对象。这些added in 2000和清理模块字典至少会从这样的循环中删除模块__dict__
。不这样做会使所有引用该模块的全局变量保持活着。
现在对aren't otherwise garbage-collectable所做的更改使垃圾收集器可以使用提供__del__
终结器的对象清除循环引用,从而无需清除模块__dict__
对于大多数情况。代码为PEP 442,但只有在__dict__
属性仍然存在且即使将sys.modules
的内容移动到弱引用并且在解释器关闭时启动GC集合时,才会触发此代码;模块终结器只是递减它们的引用计数。
答案 1 :(得分:0)
bottom of the threading docs上有少量相关文档:
其次,所有导入尝试必须在解释器开始关闭之前完成。 [..]未遵守此限制将导致解释器关闭期间出现间歇性异常和崩溃(因为后期导入尝试访问不再处于有效状态的机器)。