16

让我们面对现实吧,更改后重新加载 python 代码的整个业务是一团糟。不久前我发现调用import <module>解释器比调用更好from <module> import <class/function>,因为这样我就可以调用reload(module)来获取更新的代码。

但我现在有更复杂的问题。所以我有这个文件,module1.py,在顶部它说:

from module2 import <class1>, <function1>, etc.

然后我去修改module2里面的代码。事实证明,调用reload(module1)不会重新加载在 module2 中更改的代码,即使来自 module2 的代码是在 module1 的顶部导入的。有没有办法在不重新启动解释器的情况下重新加载所有内容?

在任何人谈论我的风格之前,我只想说:

  1. 我只reload从解释器调用,从不在活动代码中调用。这个问题与我测试新代码时有关。
  2. 我从不打电话<module> import *,我知道这会破坏可读性
4

6 回答 6

13

看看 IPython。它具有autoreload扩展,可在解释器会话期间自动重新加载模块,然后再调用其中的函数。我引用了登录页面中的示例:

In [1]: %load_ext autoreload

In [2]: %autoreload 2

In [3]: from foo import some_function

In [4]: some_function()
Out[4]: 42

In [5]: # open foo.py in an editor and change some_function to return 43

In [6]: some_function()
Out[6]: 43
于 2013-06-28T17:13:04.670 回答
10

要重新加载模块,您必须使用reload,并且必须在要重新加载的模块上使用它。重新加载模块不会递归地重新加载该模块导入的所有模块。它只是重新加载那个模块。

导入模块时,会存储对它的引用,以后对该模块的导入会重新使用已导入、已存储的版本。当您重新加载module1时,它会重新运行该from module2 import ...语句,但这只是重用已导入的版本,module2而不重新加载它。

解决此问题的唯一方法是更改​​您的代码,使其import module2代替(或补充)from module2 import .... import module除非模块本身已被导入并绑定到名称(即,使用语句,而不仅仅是语句),否则您无法重新加载模块from module import stuff

请注意,您可以使用两种形式的导入,并且重新加载导入的模块会影响后续from导入。也就是说,您可以这样做:

>>> import module
>>> from module import x
>>> x
2
# Change module code here, changing x to 3
>>> reload(module)
>>> from module import x
>>> x
3

这对于交互式工作很方便,因为它允许您使用简短的、无前缀的名称来引用您需要的内容,同时仍然能够重新加载模块。

于 2012-12-23T23:10:43.413 回答
2

好的,我不确定在不更改代码的情况下是否有资格作为答案,但是……至少,这不涉及对module1.

您可以使用一些模块包装类,它在加载 module1 之前和之后保存加载的模块,并提供一个reload方法,如下所示:

import sys

class Reloader(object):
    def __init__(self, modulename):
        before = sys.modules.keys()
        __import__(modulename)
        after = sys.modules.keys()
        names = list(set(after) - set(before))
        self._toreload = [sys.modules[name] for name in names]

    def do_reload(self):
        for i in self._toreload:
            reload(i)

然后加载 module1:

reloader = Reloader('module1')

假设您可以修改 module2 并将其重新加载到解释器中:

reloader.do_reload()
于 2013-07-01T22:26:08.943 回答
2

与其在重新加载模块方面做得更好,不如在重新启动解释器方面做得更好。例如,您可以将设置代码放入自己的文件中,然后像这样运行它:

$ python -i setup.py
>>>

这将运行 setup.py,然后将您留在交互式提示符下。或者,与其在交互式提示中做大量工作,不如编写自动化测试来为您完成工作。

你是对的,在 Python 中重新加载模块是一团糟。该语言的语义使得在进程运行时更改代码变得困难。学会不需要重新加载模块,你会更快乐。

于 2012-12-23T23:48:28.657 回答
1

不要忘记导入实际上只是在命名空间中分配名称。因此,您可以在重新加载后重新分配该名称:

>>> reload(module2)
>>> module1.class1 = module2.class1

现在class1module1 中的对象指的是从 module2 重新加载的版本。

于 2012-12-23T23:28:47.240 回答
0

这是您可以使用的递归重新加载函数(归功于@Matthew): https ://stackoverflow.com/a/17194836/1020625

于 2013-07-02T14:31:20.450 回答