重新加载函数无法擦除已删除的变量

Reload function fails to erase removed variables

我正在尝试从正在修改的 模块 访问变量,而主要的 script 运行s 通过使用重新加载。但是 reload 函数无法擦除已从 module 中删除的变量。如何强制 python 删除它们?

这是我的代码

我的module.py:

a = 1
b = 2

我的主scipt.py:

import time
from importlib import reload

import module

while True:
    reload(module)
    print('------- module reloaded')
    try:
        print('a: ', module.a)
    except AttributeError:
        print('a: ', 'undefined')
    try:
        print('b: ', module.b)
    except AttributeError:
        print('b: ', 'undefined')
    try:
        print('c: ', module.c)
    except AttributeError:
        print('c: ', 'undefined')

    time.sleep(5)

正如预期的那样,如果我 运行 我的 脚本 和 python (3.5.1) 我得到输出:

------- module reloaded
a:  1
b:  2
c:  undefined

但是当我按如下方式更改 module.py 时,出现了意外行为:

# a = 1
b = 3
c = 4

我有以下输出:

------- module reloaded
a:  1
b:  3
c:  4

这意味着 reload 正确更新了 b 的值并添加了新变量 c.但是它未能擦除已从 module 中删除的变量 a。它似乎只对在新版本 模块 中找到的变量执行更新。如何强制 reload 函数删除删除的值?

感谢您的帮助

这是设计使然。来自 docs:

When a module is reloaded, its dictionary (containing the module’s global variables) is retained. Redefinitions of names will override the old definitions, so this is generally not a problem. If the new version of a module does not define a name that was defined by the old version, the old definition remains.

通常,reload 是为了在交互式口译会话中方便;它并不是真的要在真实脚本中使用。您可能应该重新考虑您的设计。 (例如,module 不能成为常规文本文件是否有原因?)

这里的问题是 reload 是用代码实现的,粗略地说,exec 是现有缓存模块命名空间中模块代码的当前版本。这样做的原因是 reload 旨在产生全球影响; reload(module) 不只是为 you 重新加载它,它会更改每个其他模块的 module 的个人导入副本。如果它创建了一个 new 模块命名空间,其他模块仍然会缓存旧模块;虽然在 execing 之前擦除旧模块的内容可能会有帮助,但它可能会破坏已经导入的包的子模块,触发竞争条件(线程可能会在 [=12] 之前和之后看到 module.a =], 但它会在 reload) 期间神秘消失片刻

作为the docs note:

When a module is reloaded, its dictionary (containing the module’s global variables) is retained. Redefinitions of names will override the old definitions, so this is generally not a problem. If the new version of a module does not define a name that was defined by the old version, the old definition remains.

如果您绝对必须这样做,有一些解决方法可以绕过此安全机制。最简单的是简单地从模块缓存中删除模块并重新导入它,而不是 reloading 它:

import sys  # At top of file

del sys.modules['module']
import module

这不会更新该模块的任何其他导入器(它们将保留陈旧的缓存),但如果该模块仅在您的模块中使用,那将起作用。

另一种 可能 有效的方法(未经测试,有点疯狂)是在 [=12= 之前从模块中显式删除所有 public 名称] 与类似的东西:

# Intentionally done as list comprehension so no modification to module's globals dict
# occurs while we're iterating it
# Might make sense to use dir or iterate module.__all__ if available instead of using vars;
# depends on design
for name in [n for n in vars(module) if not n.startswith('_')]:
    try:
        delattr(module, name)
    except Exception:
        pass  # Undeletable attribute for whatever reason, just ignore and let reload deal with it
reload(module)  # Now exec occurs in clean namespace

这将避免过时的缓存问题,以换取精神错乱。

真的,答案是“不要使用依赖生产的设计 reloading”;如果模块只是数据,将其存储为 JSON 文件或类似文件并重新解析它(通常比 Python 导入机器便宜 很多 通过导入模块)。