阻止Python缓存导入的模块

Oli*_*ier 48 python import python-module ipython python-import

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

问题是指令import module只读取模块一次,即使该模块已经改变!因此,每次我更改包中的内容时,都必须退出并重新启动IPython.痛苦.

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

我尝试了几种方法,但都没有效果.特别是我遇到了非常非常奇怪的错误,就像一些模块或变量神秘地变得相等None......

我找到的唯一明智的资源是从pyunit 重新加载Python模块,但我没有检查它.我想要那样的东西.

一个很好的替代方案是让IPython重启,或以某种方式重启Python解释器.

那么,如果你用Python开发,你找到了什么解决方案来解决这个问题?

编辑

为了清楚起见:很明显,我理解一些旧的变量取决于模块的先前状态可能会存在.那个我能接受.为什么在Python中如此难以强制重新加载模块而不会发生各种奇怪的错误?

更具体地说,如果我将整个模块放在一个文件中,module.py那么以下工作正常:

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

obj = module.my_class()
Run Code Online (Sandbox Code Playgroud)

这段代码很漂亮,我可以在不退出IPython的情况下开发数月.

但是,每当我的模块由多个子模块组成时,地狱就会松动:

import os
for mod in ['module.submod1', 'module.submod2']:
    try:
        del sys.module[mod]
    except AttributeError:
        pass
# sometimes this works, sometimes not. WHY?
Run Code Online (Sandbox Code Playgroud)

为什么我的模块在一个大文件或几个子模块中是如此不同?为什么这种方法不起作用?

Mat*_*son 24

import检查模块是否在sys.modules,如果是,则返回它.如果要导入从磁盘加载模块,则可以先删除相应的密钥sys.modules.

有一个reload内置函数,给定一个模块对象,它将从磁盘重新加载,并将被放入sys.modules.编辑 - 实际上,它将从磁盘上的文件重新编译代码,然后在现有模块中重新评估它__dict__.可能与制作新模块对象有很大不同.

迈克格雷厄姆是对的; 如果您甚至有一些活动对象引用您不再需要的模块内容,那么重新加载就很难了.现有对象仍将引用它们实例化的类是一个明显的问题,但是通过方法创建的所有引用from module import symbol仍将指向旧版本模块中的任何对象.许多巧妙的错误都是可能的.

编辑:我同意重新启动解释器的共识是迄今为止最可靠的事情.但出于调试目的,我想你可以尝试类似下面的内容.我确定有一些不适用的极端情况,但是如果你没有做任何过于疯狂的事情(否则)你的软件包加载模块,它可能会有用.

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__)
Run Code Online (Sandbox Code Playgroud)

我非常简单地测试过这样:

import email, email.mime, email.mime.application
reload_package(email)
Run Code Online (Sandbox Code Playgroud)

打印:

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
Run Code Online (Sandbox Code Playgroud)

  • 我尝试了这些方法,通过删除`sys.modules`中的相应条目,并使用`reload`.它偶尔会起作用,但有时会产生非常非常微妙和奇怪的错误,其中一些变量突然变成"无",完全没有任何理由. (3认同)
  • "医生,我这样去的时候很痛","好吧,不要这样做".我一直把`reload`视为调试时的一个方便,并认为它是一个文档错误,它为实际的实时加载规定了它给出的语义错误有多糟糕. (3认同)

Mik*_*ham 12

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

  • 迷信!即使在[在PyUnit](http://pyunit.sourceforge.net/notes/reloading.html)这样的一般情况下,也可以相对优雅地实现热重新加载,但在特定情况下,设置它并不需要很长时间并且能够获得回报.好.例如,在我的方案中,有一个轻量级的主模块,其中包含一个单例,我的所有其他实例都挂起,并且只调用它们的方法,而不是函数.重新加载是`为每个i:del sys.modules [mymodule_i]`,`reload(..)`-ing它们,然后将每个实例的`__class__`从old换成新的. (5认同)
  • 可调试,易懂的代码简单明了.以这种方式增加复杂性是不明智或不合理的.我不建议像以前那样光滑地做任何事情. (4认同)

ojd*_*jdo 11

使用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
Run Code Online (Sandbox Code Playgroud)

  • 我发现虽然“%aut​​oreload 2”在层次结构较深时不起作用,但此解决方案始终有效:http://stackoverflow.com/a/13096672/311567,我希望它会成为 ipython 中的默认设置 (2认同)

Mit*_*dra 8

对于 Python 3.4 及以上版本

import importlib 
importlib.reload(<package_name>) 
from <package_name> import <method_name>
Run Code Online (Sandbox Code Playgroud)

有关详细信息,请参阅以下文档


pel*_*son 5

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

IPython.lib.deepreload 模块允许您递归地重新加载模块:对其任何依赖项所做的更改都将重新加载而无需退出。要开始使用它,请执行以下操作:

http://ipython.org/ipython-doc/dev/interactive/reference.html#dreload

它在 IPython notebook(至少我的版本,运行 v2.0)中作为“全局”可用。

HTH