importlib.reload 是否应该在 Python 3.6 中恢复已删除的属性?

Should importlib.reload restore a deleted attribute in Python 3.6?

我正在调查这两个相关问题: and

我在 Python 3.6 中看到了我不期望的行为,这与在 Python 2.7(和 3.4)中使用普通 reload 的行为不同。也就是说,似乎在模块初始化期间或在重新加载期间重新执行模块时将填充的模块属性在使用 del 删除其本地名称后不会恢复......见下文:

对于Python 3.6:

In [1]: import importlib

In [2]: import math

In [3]: del math.cos

In [4]: math.cos
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-4-05b06e378197> in <module>()
----> 1 math.cos

AttributeError: module 'math' has no attribute 'cos'

In [5]: math = importlib.reload(math)

In [6]: math.cos
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-6-05b06e378197> in <module>()
----> 1 math.cos

AttributeError: module 'math' has no attribute 'cos'

In [7]: importlib.reload(math)
Out[7]: <module 'math' from '/home/ely/anaconda/envs/py36-keras/lib/python3.6/lib-dynload/math.cpython-36m-x86_64-linux-gnu.so'>

In [8]: math.cos
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-8-05b06e378197> in <module>()
----> 1 math.cos

AttributeError: module 'math' has no attribute 'cos'

对于 Python 2.7(和 ):

In [1]: import math

In [2]: del math.cos

In [3]: math.cos
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-3-05b06e378197> in <module>()
----> 1 math.cos

AttributeError: 'module' object has no attribute 'cos'

In [4]: reload(math)
Out[4]: <module 'math' from '/home/ely/anaconda/lib/python2.7/lib-dynload/math.so'>

In [5]: math.cos
Out[5]: <function math.cos>

我已经尝试从 source code down to the C-level module exec function 中寻找 importlib 的细节,但我看不到任何导致它无法将重新初始化的 cos 属性写回模块范围全局变量的模块字典。

我怀疑这是 C 级重新执行逻辑中的某种类型的错误,该逻辑查看模块字典中找到的属性名称(以前导入时存在的那个,并且可能会发生变异删除了一个属性,就像在我的例子中一样),然后当使用 exec 将模块的执行副作用写入该字典时,它会跳过不存在的键名(如 cos)在模块的命名空间中,这与 Python 2.7 行为不同。

我认为这是 PEP 489 的(有意的?无意的?)效果,扩展模块初始化的彻底检查。 PEP 包括以下部分:

Module Reloading

Reloading an extension module using importlib.reload() will continue to have no effect, except re-setting import-related attributes.

Due to limitations in shared library loading (both dlopen on POSIX and LoadModuleEx on Windows), it is not generally possible to load a modified library after it has changed on disk.

Use cases for reloading other than trying out a new version of the module are too rare to require all module authors to keep reloading in mind. If reload-like functionality is needed, authors can export a dedicated function for it.

在实现 PEP 489 的提交中引入了似乎对此行为负责的 code change

甚至 Python 3.4 也不支持真正从更改的文件中重新加载扩展模块;最接近的是 save a copy of the module's dict after initialization and copy the contents back into the module's actual dict on a reload. That code still exists 的代码,但它不再触发重新加载,我不知道它是否曾经打算被触发重新加载。我相信代码目前仅用于子解释器。