重新加载组件Y,使用'from X import Y'导入。

137
在Python中,一旦我在解释器会话中使用import X导入了一个模块X,并且该模块在外部发生了变化,我可以使用reload(X)重新加载该模块。然后,这些变化将在我的解释器会话中生效。
我想知道的是,当我使用from X import Y从模块X中导入一个组件Y时,是否也可以实现这样的操作。 reload Y这样的语句是无效的,因为Y本身不是一个模块,而只是一个模块内部的组件(在这种情况下是一个类)。
是否有可能在不离开解释器会话(或导入整个模块)的情况下重新加载模块的单个组件?
为了澄清,这个问题是关于从模块X中导入一个类或函数Y并在更改时重新加载,而不是从包X中导入模块Y。

我认为这个问题存在矛盾之处:“从模块X导入组件Y是否可能”与“问题是从模块Y导入类或函数X”。我会添加一个编辑来解决这个问题。 - Catskul
1
似乎标记的答案实际上并没有回答问题,我相信我的回答是正确的。你能更新/评论一下吗? - Catskul
9个回答

142

答案

根据我的测试,标记的答案,即建议使用简单的reload(X),是不起作用的。

根据我的观察,正确的答案是:

from importlib import reload # python 2.7 does not require this
import X
reload( X )
from X import Y

测试

我的测试如下(Python 2.6.5 + bpython 0.9.5.2)

X.py:

def Y():
    print "Test 1"

bpython:

>>> from X import Y
>>> print Y()
Test 1
>>> # Edit X.py to say "Test 2"
>>> print Y()
Test 1
>>> reload( X )  # doesn't work because X not imported yet
Traceback (most recent call last):
  File "<input>", line 1, in <module>
NameError: name 'X' is not defined
>>> import X
>>> print Y()
Test 1
>>> print X.Y()
Test 1
>>> reload( X ) # No effect on previous "from" statements
>>> print Y()
Test 1
>>> print X.Y() # first one that indicates refresh
Test 2
>>> from X import Y
>>> print Y()
Test 2 
>>> # Finally get what we were after

4
哇,我觉得这个非常有用。谢谢!现在我把它作为一行代码使用:导入 X;重新加载(X);从 X 中导入 Y。 - otterb
2
这是一个比被接受的回答更好的答案。警告人们是公平的,但每个人的使用情况都不同。在某些情况下,重新加载类确实很有帮助,例如当您正在使用Python控制台并希望加载对代码的更改而不会丢失会话时。 - nicb
2
这似乎并不总是有效。我有一个名为Foo的模块,它有一个__init__.py文件,用于获取一个子模块... 我将发布一个答案作为反例。 - Jason S
1
Python 3 的一行代码:import importlib;import X; importlib.reload( X ); from X import Y - Wayne
在这个答案中,你现在必须导入x,对吧? - JobHunter69
它很丑,但这是我所知道的唯一能完成工作的方法...谢谢。 - Rayhunter

56
如果 Y 是一个模块 (X 是一个包),reload(Y) 就会很好 -- 否则,你就会明白为什么好的 Python 风格指南 (比如我的雇主所写的) 要求永远不要除了模块之外导入任何东西(这是众多优秀理由之一 -- 但人们仍然继续直接导入函数和类,无论我解释多少次都不是个好主意;-))。

3
我明白你的意思。你能否详细阐述其他不好的理由呢? - cschol
9
“Python之禅”的最后一句话是“尽管实现可能会比想象中更加困难,但不要放弃”,你可以在交互式提示符中使用“import this”命令来查看。同时,命名空间之所以是一个很棒的概念有许多原因:它们提供了立即本地的视觉线索,表明正在查找名称;易于在测试中进行模拟/注入;能够重新加载;模块可以通过重新定义某些条目来灵活改变;在序列化和恢复数据时具有可预测和可控的行为(例如通过pickling和unpickling)等等——这个丰富而长久的论点,仅凭一个SO评论远远不足以涵盖所有内容! - Alex Martelli
4
请注意,在Python 3中,reload不再在默认命名空间中,而是被移动到了importlib包中。 importlib.reload(Y) https://docs.python.org/3.4/library/importlib.html?highlight=reload#functions 另请参见https://dev59.com/HXNA5IYBdhLWcg3wcNbF。 - flies
5
@ThorSummoner,绝对不行 - 它的意思是“始终导入模块”,所以“from my.package import mymodule”是完全可以的,而且实际上更受欢迎 - 只是永远不要导入类、函数等 - 永远只导入模块。 - Alex Martelli
4
踩。为什么?这不是正确的答案。正确答案是由Catskul在2012年7月30日15:04给出的。 - meh
显示剩余5条评论

29
from modulename import func

import importlib, sys
importlib.reload(sys.modules['modulename'])
from modulename import func

1
这是我认为最好的方法,因为您可能不记得它是如何导入的。 - benathon
2
这是原问题的唯一可行解决方案,但得票太少了! - CharlesB
在Python2中,可以做同样的事情,但不需要导入importlib模块 - 只需调用reload()函数即可,它是Python2中的一个独立命令。 - mirek

6

首先,如果可以避免,您根本不应该使用reload。但是让我们假设您有自己的原因(即在IDLE内进行调试)。

重新加载库不会将名称重新加入模块的命名空间中。要做到这一点,只需重新分配变量:

f = open('zoo.py', 'w')
f.write("snakes = ['viper','anaconda']\n")
f.close()

from zoo import snakes
print snakes

f = open('zoo.py', 'w')
f.write("snakes = ['black-adder','boa constrictor']\n")
f.close()

import zoo
reload(zoo)
snakes = zoo.snakes # the variable 'snakes' is now reloaded

print snakes

你可以用其他几种方法实现这个功能。你可以通过搜索本地名称空间并重新分配与该模块相关的任何内容来自动化该过程,但我认为我们足够邪恶了。

5

如果您正在使用Jupyter环境,并且已经导入了from module import function,那么可以通过以下方式使用魔术函数 autoreload

%load_ext autoreload
%autoreload
from module import function

在IPython中引入了autoreload,介绍可以在这里找到。

4

如果你想要做这件事情:

from mymodule import myobject

请改为以下方式:

import mymodule
myobject=mymodule.myobject

现在你可以像原计划一样使用myobject(无需在代码中到处写繁琐的、难以理解的mymodule引用)。

如果你正在交互式工作并想要重新加载来自mymodule的myobject,现在可以使用以下命令:

reload(mymodule)
myobject=mymodule.myobject

3

假设你使用了 from X import Y,你有两个选项:

reload(sys.modules['X'])
reload(sys.modules[__name__]) # or explicitly name your module

或者

Y=reload(sys.modules['X']).Y

几点需要注意:

A. 如果导入的范围不是整个模块(例如:在函数中导入),则必须使用第二个版本。

B. 如果从另一个模块(Z)中将Y导入X,则必须重新加载Z,然后重新加载X,最后重新加载您的模块。即使重新加载所有模块(例如:使用[reload(mod) for mod in sys.modules.values() if type(mod) == type(sys)]),也可能在重新加载Z之前重新加载X - 然后不刷新Y的值。


1
  1. reload() 模块 X
  2. X 中导入 Y 并使用 reload() 重新加载。

请注意,重新加载不会更改在其他命名空间中绑定的已创建对象(即使您遵循 Alex 的风格指南)。


1

针对AlexMartelliCatskul的回答,有一些非常简单但却很棘手的情况似乎会使reload失效,至少在Python 2中是这样。

假设我有以下源代码树:

- foo
  - __init__.py
  - bar.py

包含以下内容:

init.py:

from bar import Bar, Quux

bar.py:

print "Loading bar"

class Bar(object):
  @property
  def x(self):
     return 42

class Quux(Bar):
  object_count = 0
  def __init__(self):
     self.count = self.object_count
     self.__class__.object_count += 1
  @property
  def x(self):
     return super(Quux,self).x + 1
  def __repr__(self):
     return 'Quux[%d, x=%d]' % (self.count, self.x)

不使用reload也可以正常工作:

>>> from foo import Quux
Loading bar
>>> Quux()
Quux[0, x=43]
>>> Quux()
Quux[1, x=43]
>>> Quux()
Quux[2, x=43]

但是尝试重新加载,它要么没有效果,要么会破坏事情:

>>> import foo
Loading bar
>>> from foo import Quux
>>> Quux()
Quux[0, x=43]
>>> Quux()
Quux[1, x=43]
>>> reload(foo)
<module 'foo' from 'foo\__init__.pyc'>
>>> Quux()
Quux[2, x=43]
>>> from foo import Quux
>>> Quux()
Quux[3, x=43]
>>> reload(foo.bar)
Loading bar
<module 'foo.bar' from 'foo\bar.pyc'>
>>> Quux()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "foo\bar.py", line 17, in __repr__
    return 'Quux[%d, x=%d]' % (self.count, self.x)
  File "foo\bar.py", line 15, in x
    return super(Quux,self).x + 1
TypeError: super(type, obj): obj must be an instance or subtype of type
>>> Quux().count
5
>>> Quux().count
6
>>> Quux = foo.bar.Quux
>>> Quux()
Quux[0, x=43]
>>> foo.Quux()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "foo\bar.py", line 17, in __repr__
    return 'Quux[%d, x=%d]' % (self.count, self.x)
  File "foo\bar.py", line 15, in x
    return super(Quux,self).x + 1
TypeError: super(type, obj): obj must be an instance or subtype of type
>>> foo.Quux().count
8

我能确保重新加载bar子模块的唯一方法是reload(foo.bar); 我访问重新加载的Quux类的唯一方法是从重新加载的子模块中获取它; 但是foo模块本身仍然持有原始的Quux类对象,可能是因为它使用了from bar import Bar, Quux(而不是import bar,然后是Quux = bar.Quux); 此外,Quux类与自身不同步,这很奇怪。

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