如何在Python Shell中重新加载一个类?

83
如果我导入一个定义了与父包同名的类的模块,它会被导入为Class而不是Module,因为父包中的__init__.py文件。详情请见different import results at different directories。在Python shell或ipython shell中,如果我执行:

from MyPak import MyMod

MyModule总是以Class形式导入,因此我无法重新加载它(reload()仅适用于模块)。再次执行

from MyPak import MyMod

似乎不会更新类定义。有人能建议一种在python shell中更新类的方式吗?

ps. 不需要重新启动python解释器。

pps. 如果你手头有代码并想测试一下:我实际上正在谈论BioPython,并且我正在处理Bio.PDB.PDBParser。我有一个ipython shell(v0.10)并编辑了PDBParser.py。只是没有办法在ipython中重新加载它。

所以,这是我做过的事情:

# start ipython v0.10
import Bio
from Bio.PDB import PDBParser
p = PDBParser()
s = p.get_structure()
# then I make changes,e.g. simply print some text, in PDBParser.py
del Bio
del PDBParser
del s
import Bio  # or reload(Bio) without deleting all the objects
from Bio.PDB import PDBParser
p = PDBParser()
s = p.get_structure() # expected output after change not seen :(

我无法看到打印出来的文本。一些更改并没有被应用。


1
CTRL+C$ pythonfrom MyPak import MyMod ;) - Jacob
import MyPak.MyMod 然后 reload(MyPak.MyMod) 会起作用吗? - agf
不,import MyPak.MyMod 仍将 MyPak.MyMod 导入为一个 - HongboZhu
1
如果使用 del MyMod,然后再使用 from MyPak import MyMod 呢?你的包布局有点不寻常(MyPak/init.py 导入了所有内容),但这可能有助于清除命名空间并允许重新导入。 - Kirk Strauser
@Kirk Strauser:仍然没有运气。事实上,我以前做过这件事。我深感困惑。 - HongboZhu
7个回答

105

仅适用于Python 3,导入reload函数:

>>> from importlib import reload

在Python 2.x和3.x上,你只需要调用reload函数即可重新加载模块:

>>> import MyPak
>>> reload(MyPak)
>>> from MyPak import MyMod

然而,旧类的实例将不会被更新(因为没有描述更新机制的代码)。


刚刚测试了一下,似乎这并没有更新MyMod。实际上,我的路径是MyPak1.MyPak2.MyMod。但我不认为这应该是原因 :( - HongboZhu
5
你使用的Python 3.3版本对我不起作用。你使用的是哪个版本? - AllTradesJack
2
@AllTradesJack 在 Python 3 中,你需要先导入 reload:from importlib import reload - ostrokach
3
在 Python3.6 中,我尝试使用此方法时,对于类(class)并没有起作用。 - Josh
2
@Josh,你只能重新加载模块,而不能重新加载类本身。 - powlo
显示剩余3条评论

48

我终于找到了答案:

import MyPak
from MyPak import MyMod

编辑了MyPak/MyMod.py文件之后,需要重新加载文件中的类MyMod,方法如下:

import sys
del sys.modules['MyPak.MyMod'] 
reload(MyPak)
from MyPak import MyMod

注意事项:

  1. 执行 del MyPakdel MyModdel MyPak.MyMod 并不能解决问题,因为它仅仅是移除了名称绑定。Python 仅从 sys.modules 中查找模块是否已经被导入。可以查看这篇帖子中的讨论:module name in sys.modules and globals()

  2. 当重新加载 MyPak 时,Python 尝试在 MyPak/__init__.py 中执行 from MyMod import MyMod。然而,它在 sys.modules 中发现了 MyPak.MyMod,因此即使 MyPak/MyMod.py 已更新,也不会重新加载 MyMod。你会发现没有新的 MyPak/MyMod.pyc 文件生成。


2
在 3.3 版本中,我无法识别 reload 作为一个命令。您使用的是哪个版本? - AllTradesJack
鉴于第二个警告,也许这个方法可以行得通:import MyPak.MyMod; reload(MyPak.MyMod); import MyPak; reload(MyPak); from MyPak import MyMod。但是,这种方法在处理更复杂的情况时(从可读性角度来看)并不适用。建议的解决方案似乎更加简洁,因为它似乎保证了模块从环境中被删除。 - Evgeni Sergeev
在3.3版本中,执行from imp import reload命令将reload()函数引入系统。 - Yona
在3.5版本中,我在使用importlib.reload()之前先执行了import importlib - Daniel

23

有三种方法可以解决这个问题:

1. 使用 import MyPak.MyMod 替代 from MyPak import MyMod

然后你可以这样写:

from importlib import reload  # If on Python 3
import MyPak.MyMod
reload(MyPak.MyMod)

它有效。

2. 使用IPython.lib.deepreload

from MyPak import MyMod
from IPython.lib.deepreload import reload
reload(MyPak)  # This should also reload all submodules

3. 使用autoreload魔法命令

%load_ext autoreload
%autoreload 2
import MyPak.MyMod  # All changes to MyPak.MyMod will be loaded automatically

1
我最近差点自己编写了一个深度重载功能...很高兴我没有浪费时间。这是一个非常有用的工具。 - FinanceGuyThatCantCode

13

你可以使用一个魔术函数:

%load_ext autoreload
%autoreload 2
from MyPak import MyMod

它也适用于函数导入:

%load_ext autoreload
%autoreload 2
from anypythonfile import my_function

这个神奇的函数在 Python 2.x(我已经在2.7+上测试过)和 Python 3.x(我已经在3.7上测试过)中都有效。


6

我有一个名为myfile.py的文件,其中包含一个名为MyClass的类。

要导入,只需执行以下操作:

from myfile import MyClass
mc = MyClass()

重新加载页面:
import sys
del sys.modules['myfile']
from myfile import MyClass
modifiedmc = MyClass()

当构建模块时,这非常有用。可以将它们放在函数中,然后只需调用该函数。

def myreload():
   import sys
   del sys.modules['myfile']
   from myfile import MyClass
   modifiedmc = MyClass()
   global mc
   mc = modifiedmc

0

如果你使用Jupyter笔记本,这里是最简短的答案。您需要更改单元格:

from your_function_name import YourClassName

from importlib import reload
import YourClassName
reload(YourClassName)
from YourClassName import your_function_name 

0

在我的电脑上,使用Python 3.5.2可以正常运行:

import importlib
importlib.reload(class)
from class import module

唯一一个展示如何导入“reload”函数的人。 - VoidMain

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接