重新加载已导入另一个模块的模块
Posted
技术标签:
【中文标题】重新加载已导入另一个模块的模块【英文标题】:reloading module which has been imported to another module 【发布时间】:2012-12-10 12:36:13 【问题描述】:让我们面对现实吧,更改后重新加载 python 代码的整个业务是一团糟。不久前我发现在解释器上调用import <module>
比from <module> import <class/function>
更好,因为这样我就可以调用reload(module)
来获取更新的代码。
但我现在有更复杂的问题。所以我有这个文件,module1.py,在顶部它说:
from module2 import <class1>, <function1>, etc.
然后我去更改 module2 中的代码。结果表明调用reload(module1)
不会重新加载在module2 中更改的代码,即使来自module2 的代码是在module1 的顶部导入的。有没有办法在不重新启动解释器的情况下重新加载所有内容?
在任何人谈论我的风格之前,我只想说:
-
我只从解释器调用
reload
,从不在活动代码中调用。这个问题与我在测试新代码时有关。
我从不给<module> import *
打电话,我知道这会破坏可读性
【问题讨论】:
这是一个可以使用的递归重新加载函数:***.com/a/17194836/1020625 等等,这是一个非常好的答案。如果您将其发布为答案,那么我会接受。 对于from module import stuff
导入,这不会满足您的要求。请参阅我对答案的评论。
【参考方案1】:
要重新加载模块,您必须使用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
这对于交互式工作来说很方便,因为它允许您使用简短的、不带前缀的名称来引用您需要的内容,同时仍然能够重新加载模块。
【讨论】:
为了完整起见,这里是重新加载的参考:docs.python.org/2/library/functions.html#reload【参考方案2】:不要忘记导入实际上只是在命名空间中分配一个名称。因此,您可以在重新加载后重新分配该名称:
>>> reload(module2)
>>> module1.class1 = module2.class1
现在 module1 中的 class1
对象指的是从 module2 重新加载的版本。
【讨论】:
【参考方案3】:与其在重新加载模块方面做得更好,不如在重新启动解释器方面做得更好。例如,您可以将设置代码放入自己的文件中,然后像这样运行它:
$ python -i setup.py
>>>
这将运行 setup.py,然后让您进入交互式提示。或者,与其在交互式提示中做大量工作,不如编写自动化测试来为您完成工作。
你是对的,在 Python 中重新加载模块是一团糟。该语言的语义使得在进程运行时更改代码变得困难。学会不需要重新加载模块,你会更快乐。
【讨论】:
有趣的想法,但有时解释器的命令历史很有用。你真的每次修改代码都销毁它吗? IPython 怎么样?它保存会话之间的命令历史记录。 使用PYTHONSTARTUP
环境变量会更有意义。 @EricWilson,您可以使用 readline.read_history_file("setup.py")
阅读相同的启动脚本,将所有命令添加到您的历史记录中。 @utapyngo,你不需要 IPython; readline
模块提供了该功能:atexit.register(readline.write_history_file, hist_file)
,其中hist_file
是您要包含历史记录的文件的名称。【参考方案4】:
看看 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
【讨论】:
谢谢,但我不会使用 IPython 或 Jython。 我很高兴在这里获得赏金,因为发现 IPython 的价值远远超过 50 个代表。谢谢。【参考方案5】:好的,我不确定是否有资格在不更改代码的情况下作为答案,但是……至少,这不涉及对 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()
【讨论】:
【参考方案6】:这是您可以使用的递归重新加载函数(感谢@Matthew): https://***.com/a/17194836/1020625
【讨论】:
这不会解决 OP 询问的问题。该函数通过查找模块中引用其他模块的所有全局变量来工作。这些将由import someModule
之类的语句创建。但是如果你这样做from someModule import something
,则不会创建引用模块本身的变量,因此rreload
函数不会重新加载someModule
。
那是真的,我没想过以上是关于重新加载已导入另一个模块的模块的主要内容,如果未能解决你的问题,请参考以下文章