防止Python缓存导入的模块

防止Python缓存导入的模块,python,import,ipython,python-module,python-import,Python,Import,Ipython,Python Module,Python Import,在使用IPython用Python开发一个大型项目(分为多个文件和文件夹)时,我遇到了缓存导入模块的麻烦 问题是指令导入模块仅读取模块一次,即使该模块已更改!因此,每次我更改包中的某些内容时,我都必须退出并重新启动IPython。痛苦 有没有办法正确地强制重新加载某些模块?或者,更好的办法是以某种方式阻止Python缓存它们 我尝试了几种方法,但都不管用。特别是我遇到了非常非常奇怪的bug,比如一些模块或变量神秘地变得等于None 我找到的唯一合理的资源是pyunit,但我没有检查它。我想要那样

在使用IPython用Python开发一个大型项目(分为多个文件和文件夹)时,我遇到了缓存导入模块的麻烦

问题是指令
导入模块
仅读取模块一次,即使该模块已更改!因此,每次我更改包中的某些内容时,我都必须退出并重新启动IPython。痛苦

有没有办法正确地强制重新加载某些模块?或者,更好的办法是以某种方式阻止Python缓存它们

我尝试了几种方法,但都不管用。特别是我遇到了非常非常奇怪的bug,比如一些模块或变量神秘地变得等于
None

我找到的唯一合理的资源是pyunit,但我没有检查它。我想要那样的东西

一个好的替代方法是让IPython重新启动,或者以某种方式重新启动Python解释器

那么,如果您使用Python进行开发,您找到了解决此问题的解决方案吗

编辑

让事情弄清楚:显然,我理解一些依赖于模块先前状态的旧变量可能会存在。我没意见。为什么在Python中强制重新加载模块而不发生各种奇怪的错误是如此困难

更具体地说,如果我将整个模块都放在one文件
module.py
中,那么下面的工作就可以了:

import sys
try:
    del sys.modules['module']
except AttributeError:
    pass
import module

obj = module.my_class()
这段代码运行得很好,我可以在几个月内不退出IPython进行开发

然而,每当我的模块由几个子模块组成时,地狱就会爆发:

import os
for mod in ['module.submod1', 'module.submod2']:
    try:
        del sys.module[mod]
    except AttributeError:
        pass
# sometimes this works, sometimes not. WHY?

为什么对于Python来说,无论是在一个大文件中还是在几个子模块中,我的模块都是如此不同?为什么这种方法不起作用呢?

退出并重新启动解释器是最好的解决方案。任何类型的实时重新加载或无缓存策略都无法无缝工作,因为不再存在的模块中的对象可以存在,因为模块有时存储状态,而且即使您的用例确实允许热重新加载,它也太复杂,不值得考虑。

import
检查模块是否可用在系统模块中,如果是,则返回它。如果要导入以从磁盘加载新模块,可以先删除
sys.modules
中的相应键

有一个
reload
内置函数,给定一个模块对象,该函数将从磁盘重新加载该对象,并将其放入
sys.modules
。编辑——实际上,它将从磁盘上的文件重新编译代码,然后在现有模块的
\uuuuu dict\uuuu
中重新评估代码。可能与创建新模块对象非常不同的东西

迈克·格雷厄姆是对的;如果你甚至有一些活动对象引用了你不再需要的模块的内容,那么重新加载是很困难的。现有对象仍将引用它们从中实例化的类是一个明显的问题,但通过模块导入符号创建的所有引用仍将指向旧版本模块中的任何对象。许多微妙的错误是可能的

编辑:我同意这样的共识,即重启解释器是目前为止最可靠的事情。但出于调试目的,我想您可以尝试以下方法。我敢肯定,在某些情况下,这是行不通的,但如果您在包中加载模块时没有做任何太疯狂的事情(否则),它可能会很有用

def reload_package(root_module):
    package_name = root_module.__name__

    # get a reference to each loaded module
    loaded_package_modules = dict([
        (key, value) for key, value in sys.modules.items() 
        if key.startswith(package_name) and isinstance(value, types.ModuleType)])

    # delete references to these loaded modules from sys.modules
    for key in loaded_package_modules:
        del sys.modules[key]

    # load each of the modules again; 
    # make old modules share state with new modules
    for key in loaded_package_modules:
        print 'loading %s' % key
        newmodule = __import__(key)
        oldmodule = loaded_package_modules[key]
        oldmodule.__dict__.clear()
        oldmodule.__dict__.update(newmodule.__dict__)
我做了非常简短的测试,如下所示:

import email, email.mime, email.mime.application
reload_package(email)
印刷:

reloading email.iterators
reloading email.mime
reloading email.quoprimime
reloading email.encoders
reloading email.errors
reloading email
reloading email.charset
reloading email.mime.application
reloading email._parseaddr
reloading email.utils
reloading email.mime.base
reloading email.message
reloading email.mime.nonmultipart
reloading email.base64mime

您可以使用中描述的导入钩子机制来加载模块本身,而不是某种代理对象,它将允许您对底层模块对象执行任何操作—重新加载它,删除对它的引用等

另外一个好处是,您当前现有的代码不需要更改,并且这个额外的模块功能可以从代码中的一个点上删除—您可以将finder添加到
sys.meta\u path

关于实现的一些想法:创建同意查找任何模块(内置模块除外)的finder(您与内置模块无关),然后创建loader,返回从
types.ModuleType
子类化的代理对象,而不是真正的模块对象。请注意,loader对象不会被强制创建对加载到
sys.modules
中的模块的显式引用,但强烈建议这样做,因为正如您已经看到的,它可能会意外失败。代理对象应该捕获所有
\uuuu getattr\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
\uuuuuuuu delattr\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。您可能不需要定义
\uuu getattribute\uu
,因为您不会用代理方法隐藏真正的模块内容。所以,现在您应该以某种方式与代理通信——您可以创建一些特殊的方法来删除底层引用,然后导入模块,从返回的代理中提取引用,删除代理并保留对重新加载的模块的引用。呸,看起来很吓人,但应该可以在每次不重新加载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

这里已经有了一些非常好的答案,但是值得了解dreload,它是IPython中可用的一个函数,可以作为“深度重载”。从文件中:

IPython.lib.deepreload模块允许您递归地重新加载 模块:将重新加载对其任何依赖项所做的更改 不必退出。圣
using (Py.GIL())
        {
            dynamic mod = Py.Import(this.moduleName);
            if (mod == null)
                throw new Exception( string.Format("Cannot find module {0}. Python script may not be complied successfully or module name is illegal.", this.moduleName));

            // This command works perfect for me!
            PythonEngine.ReloadModule(mod);

            dynamic instance = mod.ClassName();
import moduleA, moduleB
from imp import reload
reload (moduleB)
import importlib 
importlib.reload(<package_name>) 
from <package_name> import <method_name>