Python命名空间:如何在其他模块中访问唯一对象?
我正在写一个中等规模的PyQt应用程序,代码量大概几千行。我一开始是想把代码写得模块化,这样更容易理解,但我在Python的命名空间规则上遇到了麻烦。在某些情况下,我需要创建一个类的唯一实例,以便其他代码可以使用。
举个例子:有一个对象代表Aspell,它作为一个子进程运行,提供一个check(word)的方法。再比如:这个应用程序里有一个QTextEdit,其他代码需要调用这个唯一对象的方法,比如“如果theEditWidget.document().isEmpty()...”
无论我在哪里创建这样的对象,它只能在那个模块的代码中被引用,其他模块无法访问。所以,比如说,编辑小部件的代码不能调用Aspell网关对象,除非Aspell对象是在同一个模块中创建的。这没问题,但其他模块也需要用到这个对象。
在这个问题中,提到了一个
好吧,其他地方建议的,这似乎是我问题的简单解决方案。我刚刚测试了以下内容:
junk_main.py:
import junk_A
singularResource = junk_A.thing()
import junk_B
junk_B.handle = singularResource
print junk_B.look()
junk_A.py:
class thing():
def __init__(self):
self.member = 99
junk_B.py:
def look():
return handle.member
当我运行junk_main时,它打印出99。所以主代码可以通过赋值将名称注入到模块中。我在想这样做的坏处是什么。
3 个回答
在很多情况下,我们需要只创建一个类的对象,这样其他代码就可以使用这个对象。
与其费劲去搞一个单例工厂,不如在程序的主要入口和需要这个对象的地方之间创建这个只用一次的对象。然后可以把这个对象作为参数传递给其他对象。这样一来,你就不会创建多个相同的对象了。
举个例子:
def main(...):
aspell_instance = ...
myapp = MyAppClass(aspell_instance)
或者...
class SomeWidget(...):
def __init__(self, edit_widget):
self.edit_widget = edit_widget
def onSomeEvent(self, ...):
if self.edit_widget.document().isEmpty():
....
我不知道这样解释是否够清楚,或者是否适合你的情况。不过说实话,我发现唯一不能这样做的情况是在基于CherryPy的网络服务器中,因为在那里的入口点几乎到处都是。
你可以像使用函数一样,通过 .
操作符来访问模块中的对象。例如:
# Module a.py
a = 3
>>> import a
>>> print a.a
3
这个例子很简单,但你可能想做一些类似这样的事情:
# Module EditWidget.py
theEditWidget = EditWidget()
...
# Another module
import EditWidget
if EditWidget.theEditWidget.document().isEmpty():
或者...
import * from EditWidget
if theEditWidget.document().isEmpty():
如果你选择使用 import * from
的方式,你甚至可以在你的模块中定义一个名为 __all__ 的列表,里面列出你想要导出的所有对象的名字(以字符串形式)。所以如果你只想导出 theEditWidget,你可以这样做:
# Module EditWidget.py
__all__ = ["theEditWidget"]
theEditWidget = EditWidget()
...
结果发现,答案比我想的要简单。正如我在问题中提到的,主模块可以给导入的模块添加名字。而且,任何代码都可以给一个对象添加成员。所以,创建一个模块间通信区域的简单方法就是在主模块里创建一个非常基础的对象,比如叫 IMC(代表模块间通信器),然后把需要让其他模块访问的内容作为成员添加进去:
IMC.special = A.thingy()
IMC.important_global_constant = 0x0001
等等。在导入任何模块后,只需把 IMC 赋值给它:
import B
B.IMC = IMC
不过,从软件设计的角度来看,这可能不是最好的主意。如果你只是让 IMC 存放命名常量,它就像 C 语言的头文件。如果只是用来访问单一资源,它就像一个外部链接。但由于 Python 的规则比较宽松,任何模块中的代码都可以修改或添加 IMC 的成员。如果使用不当,可能会出现“是谁改的?”这样的调试问题。如果有多个进程,竞争条件也是一个危险。