如何初始化一个Python对象并在其他模块中共享?

2 投票
3 回答
8207 浏览
提问于 2025-04-17 16:25

我在学习Python的时候感到非常沮丧。有一件事情我想做,就是从一个配置字典中初始化一个类的单一实例,然后在其他模块中访问这个类。

我遇到了一些问题,尝试的方法也没有奏效,希望有人能给我指点一下正确的“Python风格”的做法。

首先,我的应用程序可以作为一个twistd插件初始化,也可以作为一个独立的脚本运行。

import resource
class App(object):
  _config = None
  _someotherobject = None
  def __init__(self, config):
    self._config = config
    ....

def main():
  global myapp
  myapp = App({}) # Could use help here, how to pass config to it

myapp = None #Global doesnt work as I expect, it doesnt modify this instance, stays as None
if __name__ == '__main__':
  main()


#-----------resource.py
class Foo(object):
  def foo(self):
    app.myapp.somefunction() #NoneType object has no attribute

我已经确认在其他模块的代码执行之前,应用对象是创建好的。但我就是搞不懂为什么模块中的“全局”实例并没有按我预期的那样工作,也不太清楚如何从另一个模块引用这个实例。

----- 编辑 ------ 为了澄清几点,这个脚本是通过 python app.py 来调用的。 app.py引用了一个叫 resources.py 的模块,里面有很多类的定义。在一些类中,当执行时,它们会引用'app.myapp'的“单例”实例。

3 个回答

0

你最大的麻烦是sotapme提到的那个问题。如果你没有把app.py当作主程序来运行,那么就没有任何代码在调用app.main(),所以全局变量也就没有被初始化。

除此之外,“如何在另一个模块中引用实例”其实很简单,只要你知道一件事:

在Python中,全局变量是有命名空间的。

更具体一点,在你的其他模块中,只需import app,然后通过app.myapp来访问全局变量。

使用__name__ == '__main__'这个技巧,正如sotapme所解释的那样,意味着你可以随便import app,而不会每次都运行main()函数。

特别是,当你运行这个代码时:

python app.py

Python解释器会加载app.py,并把它的__name__设置为'__main__',这样if语句就会被触发,从而使得(模块级别的)全局变量myapp被设置为App({})

现在,当resource.py执行import app时,它的__name__会被设置为app,所以if语句就不会被触发,这样你就会构造一个新的App并替换掉全局变量。从resource.py中的代码,你可以使用app.myapp,这样你就能访问到app.py中看到的同一个对象myapp


你还问如何将配置传递给App构造函数。我不太明白你在这方面遇到的问题。你现在传递的是一个空的dict作为配置。如果你有其他的dict要传递,直接用它就行。不要这样:

myapp = App({}) # Could use help here, how to pass config to it

改成这样:

myapp = App(configdict)

如果你的问题是如何获取那个configdict,那就要看信息来自哪里。

如果你想解析一个用户可编辑的配置文件,configparser模块非常适合传统的.ini风格文件,文档中还有链接解释如何处理一些其他流行的格式。

如果你想从命令行构建配置信息,可以查看argparse

如果你想让环境变量与上述任一项互动(例如,MYAPP_CONFIG可能会告诉你的configparser代码加载一个不同的配置文件,或者MYAPP_CACHE_DIR可能会为argparse--cachedir命令行参数提供一个不同的默认值),你可以在os.environ中获取这些值,但你需要自己写代码来处理它们。

0

为了让内容更清晰,这里有一个例子:

class App:
    _config = None
    _someotherobject = None

def __init__(self, config):
    self._config = config

def main():
  myapp = App(config) # pass a config object to App

if  __name__ =='__main__':main()

在另一个应用程序中,你需要做以下事情:

from first_app import App

myapp = App(config)
3

你的 main 只会作为独立的脚本被调用,而不是从其他模块导入时被调用。

if __name__ == '__main__':
  main()

诀窍在于让你的模块可以从命令行运行。

为了证明这一点

import app
app.main()

然后运行你的代码。

一旦你初始化了它,app 就像一个单例对象,任何其他导入它的模块都会得到这个初始化后的版本。

我遇到过类似的问题,我不想直接访问 app,但希望模块能够通过 app = MyApp() 来共享相同的数据(我忘了为什么想这样做,但可能是因为我想在第一次使用时就初始化它)

最后我选择使用 Borg 而不是单例模式。

撰写回答