如何初始化一个Python对象并在其他模块中共享?
我在学习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 个回答
你最大的麻烦是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
中获取这些值,但你需要自己写代码来处理它们。
为了让内容更清晰,这里有一个例子:
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)
你的 main
只会作为独立的脚本被调用,而不是从其他模块导入时被调用。
if __name__ == '__main__':
main()
诀窍在于让你的模块可以从命令行运行。
为了证明这一点
import app
app.main()
然后运行你的代码。
一旦你初始化了它,app
就像一个单例对象,任何其他导入它的模块都会得到这个初始化后的版本。
我遇到过类似的问题,我不想直接访问 app
,但希望模块能够通过 app = MyApp()
来共享相同的数据(我忘了为什么想这样做,但可能是因为我想在第一次使用时就初始化它)
最后我选择使用 Borg 而不是单例模式。