如何从 Python 模块中重新加载它本身?

10

我有一个Python模块,其中包含许多变量。这些变量被许多其他模块使用和更改。当发生这种情况时(以进行刷新),我希望从模块本身重新加载此模块。

如何做到这一点?

更新:所以......这是我,问题作者,在问题发布日期8年后。我完全不知道当时为什么会尝试这样做。

如果您阅读此内容,请尝试使用适当的配置管理技术(大多数情况下由您的框架提供)。

# ==================================
# foo.py

spam = 100

def set_spam(value):
    spam = value
    foo = reload(foo) # reload module from itself

# ==================================
# bar.py

import foo
print foo.spam # I expect 100
# assume that value changes here from some another modules (for example, from eggs.py, apple.py and fruit.py)
foo.set_spam(200)
print foo.spam # I expect 200

6
这听起来像是一个相当糟糕的想法... - Wooble
@Wooble,好的,那你会推荐什么?将所有这些变量封装为单例类吗? - Bruno Gelb
1
在你的特定情况下,我认为你只是想要在set_spam函数的顶部使用global spam,但我不完全清楚你为什么认为重新加载可以保持值为200。如果有什么作用,它会在设置后立即将其重置为100,这毫无意义。 - Wooble
@Wooble比依赖用户reload(module)更好。 - Kermit
7个回答

3

您尝试实现的内容存在一些问题,除非您是有意设置了一个自修改代码系统,但看起来您并没有这样做。

1. 全局变量

spam = 100

def set_spam(value):
    spam = value
    foo = reload(foo) #reload module from itself

这样做不会起作用。由于 Python 闭包的工作原理,你的 spam = value 代码会在 set_spam 函数内创建一个新的本地变量 spam,而这个变量将不会被使用。要正确更改全局变量 spam 的值,您必须使用 global 关键字,如下所示:

spam = 100

def set_spam(value):
    global spam
    spam = value

2. 重新加载模块“自身”

据我所知,实际上没有办法做到这一点,也不应该需要。您已经导入的任何模块都是从其他模块调用的,直至__main__。您只需从调用模块刷新它即可。是的,您可以尝试自我导入模块(尽管可能存在无限循环问题,如mgilson所提到的),但是即使是这样(使用名为“foo”的示例),如果您让它导入自身,则只会有foo.foo,并且执行类似于foo.reload(foo)的操作(如果这个有效的话)只会重新加载子级foo,而不是基本的那一个。

3. 重新加载foo.py

# ==================================
# foo.py

spam = 100

def set_spam(value):
    global spam
    spam = value

请注意,在代码的顶部,您将100分配给spam。每次导入模块时,您都会再次执行此操作。因此,即使您已经在导入foo的代码中更改了spam的值,当您重新加载模块时,实际上您将销毁刚刚进行的更改。例如:

>>> import foo
>>> foo.spam
100
>>> foo.spam = 9
>>> foo.spam
9
>>> reload(foo)
>>> foo.spam
100

因此,如果您希望保留在foo变量中所做的更改,则不应该重新加载模块。此外,您甚至不需要使用set_spam函数来更改spam,您可以像我所做的那样直接设置它。

4. 尝试在其他模块中使用“更改后”的模块值

最后,如果我正确理解了您想要做的事情,那是行不通的。这在很大程度上是由于我在第三部分提到的一些事情,其中每次您加载foo时,spam = 100行将重置spam的值。同样,如果您在两个不同的其他模块中导入foo模块,当每个模块导入它时,它们都将独立地从spam = 100开始,而与其他模块对foo.spam的处理完全无关。例如,如果bar1.pybar2.py中都包含import foo行:

>>> import bar1, bar2
>>> bar1.foo.spam
100
>>> bar2.foo.spam
100
>>> bar1.foo.spam = 200
>>> bar1.foo.spam
200
>>> bar2.foo.spam
100

如果您能更详细地解释你想要做的事情,我们可以帮助您重构代码以使其更好地工作。


3
在Python 2.6.2中,这很简单。假设您的模块名称为“t”,并定义如下:
import imp

def reload():
    name="t"
    imp.load_module(name,*imp.find_module(name))

print("loaded")

在加载了此模块之后,添加另一个成员并执行 t.reload()。
顺便说一下,我猜每个人都认为这是个坏主意:他们可能是对的,但如果你正在交互式开发一个模块,也许这样做会更方便。在将代码分发给其他人之前,请务必将其删除,否则他们可能会感到困惑。

2

在这里

import sys

reload(sys.modules[__name__])

注意,这不适用于main模块,而是适用于任何其他模块。


1
无法前进-> ImportError:无法重新初始化内部模块__main__ - DeDenker
很好,它可以与除了“main”模块之外的其他模块一起使用。 - akuhn
这个可以工作,但是如果你在函数内部,旧版本的函数将被执行 - 但是你可以定义一个包装器 - 一个主函数带有重新加载函数调用,和一个被更新的包装函数,在重新加载后被调用。 - TarmoPikaro

2
您也可以统计模块重新加载的次数!:

mymodule.py

import importlib
import sys

count = 0
var = 0

def refresh():
    global count
    c = count
    importlib.reload(sys.modules[__name__])
    count = c + 1
    print (f'reloaded {count} time(s)')

main.py

import mymodule

mymodule.var = 'changed'
mymodule.refresh()
  # -> reloaded 1 times(s)
print(mymodule.var)
  # -> 0
mymodule.refresh()
  # -> reloaded 2 times(s)

每次重新加载模块的第四行“count = 0”,计数器都会被重置为0! - nadapez

1

我不清楚您如何在不陷入无限循环的情况下进行清理。也许最好有一个“重置”功能?

def reset():
    global spam
    spam = 100

请注意,这个设计对我来说仍然有些奇怪。从长远来看,更好的数据封装可能会对您有很大帮助——也许可以采用以下方式:
def new_namespace():
    return {'spam':100}

然后,您的其他每个函数都可以接受命名空间字典作为参数,而不是依赖于foo命名空间。


0

这个操作根本没有意义。来自Python文档

reload(module):重新加载先前导入的模块。参数必须是一个模块对象,因此它必须已成功导入。

因此,reload只有在之前执行了import的其他模块上下文中才有意义。


5
好的,它在全局的 sys.modules 中,所以它并不像听起来那么荒谬。不过... - Wooble
显然,该模块不应该从全局作用域重新加载,而应该从一个函数中重新加载,该函数应该从另一个模块中调用! - nadapez

0

这是先前答案的另一个版本,只有在计数器不存在时才初始化。在 Python 3.8.6 上进行了测试。

# mod.py
import importlib, sys

a=0

def reload():
  
    importlib.reload(sys.modules[__name__])
    global i
    try:
        i += 1
    except:
        i = 1
    print(f'reloaded {i} time(s)')

# main.py
import mod

mod.a = 10
print (mod.a) # -> 10
mod.reload() # -> reloaded 1 time
print (mod.a) # -> 0
mod.reload() # -> reloaded 2 times

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