该行为没有得到充分的记录,并且在所有版本的Python(从1.5到ish直至Python 3.4)中都存在:
作为此更改的一部分,在大多数情况下,在解释器关闭期间,不再将模块全局变量强行设置为__del__,而是依靠循环垃圾收集器的正常操作。
该行为的唯一文档是__del__源代码:
/* 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). */
请注意,将值设置为__del__是一种优化。 另一种选择是从映射中删除名称,这将导致不同的错误(尝试从sys.modules处理程序使用全局变量时,会导致不同的错误(__dict__异常,而不是__dict__s)。
正如您在邮件列表中发现的那样,此行为早于循环垃圾收集器; 它是在1998年添加的,而循环垃圾收集器是在2000年添加的。由于功能对象始终引用模块__del__,因此模块中的所有功能对象都包含循环引用,这就是为什么__dict__需要在GC发挥作用之前清除的原因。
即使添加了循环GC,它也可以保留在原位,因为循环中可能包含__del__方法的对象。 这些在其他方面是不可回收的,清理模块字典至少会从此类循环中删除模块__dict__。 不这样做将使该模块的所有引用的全局变量保持活动状态。
现在,对PEP 442所做的更改使垃圾收集器可以使用提供__del__终结器的对象清除循环引用,从而在大多数情况下无需清除模块__dict__。 代码仍然存在,但是只有在__dict__属性仍然有效的情况下才触发该代码,即使将sys.modules的内容移至弱引用并在解释器关闭时启动GC收集运行之后也是如此。 模块终结器只需减少其引用计数即可。

被折叠的 条评论
为什么被折叠?



