3

关于 SO 上的重新导入有很多问题和答案,但如果不了解其背后的机制,这一切似乎都非常违反直觉。

如果你导入一个模块,改变内容,然后再次尝试导入,你会发现第二次导入没有效果:

>>> import foo    # foo.py contains: bar = 'original'
>>> print foo.bar
original
>>> # edit foo.py and change to: bar = 'changed'
>>> import foo
>>> print foo.bar
original

当我发现reload

>>> reload(foo)
>>> print foo.bar
changed

但是,当您从模块导入项目而不导入模块本身时,没有简单的解决方案:

>>> from foo import baz
>>> print baz
original
>>> # change foo.py from baz = 'original' to baz = 'changed'
>>> from foo import baz
>>> print baz
original
>>> reload(foo)
Traceback (most recent call last):
  File "<pyshell#10>", line 1, in <module>
    reload(foo)
NameError: name 'foo' is not defined

import当你给它一个新的语句时,为什么 Python 不会更新导入的项目?

4

2 回答 2

11

导入模块时,它会缓存在sys.modules. 在同一会话中再次导入同一模块的任何尝试都只会返回其中包含的已经存在的模块。当从多个地方导入模块时,这会加快整体体验。它还允许模块在所有导入之间共享自己的对象,因为每次都返回相同的模块。

如前所述,您可以使用reload重新导入整个模块。检查文档中的警告,因为即使这也不是万无一失的。

当您从模块中导入特定项目时,整个模块将按上述方式导入,然后将您请求的对象放置在您的命名空间中。reload不起作用,因为这些对象不是模块,而且您从未收到对模块本身的引用。解决方法是获取对模块的引用,重新加载它,然后重新导入:

>>> from foo import baz
>>> print baz
original
>>> # change foo.py from baz = 'original' to baz = 'changed'
>>> import foo
>>> reload(foo)
>>> from foo import baz
>>> print baz
changed
于 2012-07-23T18:56:24.047 回答
2

“为什么”的简单解释是 import 语句涉及两个操作:加载模块(即运行其中的代码),以及将名称导入到导入模块的命名空间中。重新导入只会重做第二个操作。原因是第一个操作在计算和内存资源方面可能很昂贵。

reload提供作为重做第一个操作的一种方式,但是,如您所见,它需要模块名称。但是,不要被愚弄,以为from foo import bar不会加载整个模块。确实如此。所有模块的代码都运行,只是您只能引用bar您明确导入的名称(例如,)。因此,导入模块和不导入模块之间几乎没有真正的区别;唯一的区别是命名空间问题。所以如果你认为你可能想要重新加载模块foo,你应该继续做import foo。如果你愿意,你可以这样做来刷新你导入的名字:

import foo
from foo import bar
# later...
reload(foo)
from foo import bar

不能重新导入单个名称的另一个原因是 Python 无法更改引用的对象。它只能重新绑定名称。假设你这样做:

from foo import bar
newBar = bar
from foo import bar

即使第二次导入确实刷新了 bar 的值,它也永远不会刷新 newBar 的值,因为 foo 和 bar 都无法知道您创建了一个额外的名称来引用 bar。避免重新绑定个人名称可以防止您落入这个陷阱;如果人们认为重新导入会刷新名称,那么很容易忘记它仍然不会刷新对象。

于 2012-07-23T19:17:37.503 回答