Python的关闭过程将模块全局变量设为None的文档在哪里?
CPython在关闭时有个奇怪的行为,就是它会把模块设置为None。这会导致我写的一些多线程代码在关闭时出错记录出现问题。
我找不到关于这种行为的文档。在PEP 432中提到过:
[...] 这大大减少了会出现“模块全局变量被设置为None”这种行为的模块数量,这种行为是为了故意打破循环引用,并尝试更干净地释放更多外部资源。
关于这种行为有一些StackOverflow上的问题,而C API文档中提到嵌入式解释器的关闭行为。
我还找到了一些在python-dev上的相关讨论和一个相关的CPython bug:
这个补丁并没有改变模块对象在被释放时清空它们的全局字典的行为。
这种行为在哪里有文档说明呢?这是Python 2特有的吗?
2 个回答
在线程文档的底部有一些相关的说明:
其次,所有的导入操作必须在解释器开始关闭之前完成。[..] 如果不遵守这个规则,可能会在解释器关闭时出现间歇性的错误和崩溃(因为晚来的导入尝试去访问已经不再有效的部分)。
这个行为并没有很好地记录下来,几乎所有版本的Python,从大约1.5版本到Python 3.4都有这个特性:
作为这个变化的一部分,在大多数情况下,模块的全局变量在解释器关闭时不再强制设置为
None
,而是依赖于循环垃圾回收器的正常工作。
关于这个行为的唯一文档就是moduleobject.c
源代码:
/* 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
是一种优化;另一种做法是从映射中删除名称,这会导致不同的错误(在尝试使用__del__
处理器中的全局变量时,会出现NameError
异常,而不是AttributeError
)。
正如你在邮件列表中发现的,这种行为早于循环垃圾回收器;它是在1998年添加的,而循环垃圾回收器是在2000年添加的。由于函数对象总是引用模块的__dict__
,所以模块中的所有函数对象都涉及循环引用,这就是为什么在垃圾回收器介入之前需要清除__dict__
。
即使在添加循环垃圾回收器后,这个机制也一直保留,因为可能会有带有__del__
方法的对象参与循环。这些对象在其他情况下无法被垃圾回收,清理模块字典至少可以将模块的__dict__
从这些循环中移除。如果不这样做,模块的所有引用的全局变量都会保持活跃。
为PEP 442所做的更改现在使得垃圾回收器能够清除带有__del__
终结器的对象的循环引用,这样在大多数情况下就不需要清除模块的__dict__
了。代码仍然存在,但只有在sys.modules
的内容被移动到弱引用并在解释器关闭时启动垃圾回收时,__dict__
属性仍然存在时,才会触发这个机制;模块的终结器只是减少它们的引用计数。