Python 重新加载已导入到其他模块的模块

Python 重新加载已导入到其他模块的模块,python,import,module,reload,Python,Import,Module,Reload,让我们面对它,在修改python代码后重新加载它的整个过程是一团混乱。不久前我发现在解释器中调用import比从import调用要好,因为这样我就可以调用reload(module)来获取更新的代码 但我现在有更复杂的问题。我有一个文件module1.py,上面写着: from module2 import <class1>, <function1>, etc. 来自模块2导入等的。 然后我去修改模块2中的代码。结果表明,调用reload(module1)不会重新加载

让我们面对它,在修改python代码后重新加载它的整个过程是一团混乱。不久前我发现在解释器中调用
import
比从import调用
要好,因为这样我就可以调用
reload(module)
来获取更新的代码

但我现在有更复杂的问题。我有一个文件module1.py,上面写着:

from module2 import <class1>, <function1>, etc.
来自模块2导入等的
然后我去修改模块2中的代码。结果表明,调用
reload(module1)
不会重新加载module2中更改的代码,即使来自module2的代码是在module1的顶部导入的。有没有办法在不重新启动解释器的情况下重新加载所有内容

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

  • 我只从解释器调用
    reload
    ,从不在活动代码中调用。这个问题与我测试新代码时有关
  • 我从不从
    import*
    调用,我知道这会破坏可读性

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

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

    解决此问题的唯一方法是更改您的代码,使其能够
    导入module2
    ,而不是(或除了)
    从module2导入…
    。除非模块本身已导入并绑定到名称(即,使用
    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
    

    这对于交互式工作非常方便,因为它允许您使用简短、不固定的名称来引用所需内容,同时仍然能够重新加载模块。

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

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

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

    重新加载模块不是更好,而是更好地重新启动解释器。例如,您可以将安装代码放入自己的文件中,然后按如下方式运行:

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


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

    看看IPython。它有一个扩展,可以在解释器会话期间,在调用内部函数之前自动重新加载模块。我引用登录页上的例子:

    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
    

    好的,我不确定在不修改代码的情况下这是否符合答案,但是。。。至少,这不涉及对
    模块1
    的更改

    您可以使用一些模块包装器类,该类在加载模块1之前和之后保存加载的模块,并提供一个
    重新加载
    方法,类似于:

    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)
    
    然后,用以下各项加载模块1:

    reloader = Reloader('module1')
    
    请注意,您可以使用以下命令修改模块2并将其重新加载到解释器中:

    reloader.do_reload()
    

    下面是一个可以使用的递归重载函数(归功于@Matthew):

    这个想法很有趣,但有时解释器的命令历史记录很有用。你真的每次修改代码都会破坏它吗?IPython呢?它在会话之间保存命令历史记录。使用
    PYTHONSTARTUP
    环境变量更有意义@EricWilson,您可以使用
    readline.read\u history\u文件(“setup.py”)
    读取相同的启动脚本,将所有命令添加到历史记录中@乌塔平戈,你不需要伊皮顿;
    readline
    模块提供该功能:
    atexit.register(readline.write_history_file,hist_file)
    ,其中
    hist_file
    是您希望包含历史的文件的名称。谢谢,但我不会使用IPython或Jython。我很高兴在这里奖励奖金,因为发现IPython的价值远远超过50次。谢谢。这里有一个递归重载函数,你可以使用:等等,这是一个非常好的答案。如果您将其作为答案发布,我将接受。这不会满足您对模块导入内容的
    要求。请看我对答案的评论。这并不能解决OP提出的问题。该函数通过查找模块中引用其他模块的所有全局变量来工作。这些将由诸如
    import someModule
    之类的语句创建。但是,如果您从someModule导入一些内容,则不会创建引用模块本身的变量,因此,
    rreload
    函数将不会重新加载
    someModule
    。确实如此,为了完整性,我没有考虑它,这里是重新加载的参考: