Python 命名空间:如何使其他模块中的唯一对象可访问?

发布于 2024-12-03 05:23:10 字数 1121 浏览 1 评论 0原文

我正在编写一个中等大小(几个 KLOC)的 PyQt 应用程序。为了便于理解,我开始将其编写在漂亮的模块中,但我在 Python 命名空间的规则上陷入了困境。在某些时候,仅实例化类的一个对象作为其他代码的资源非常重要。

例如:代表 Aspell 的对象附加为子进程,提供 check(word) 方法。另一个例子:应用程序具有单个 QTextEdit,其他代码需要调用这个单个对象的方法,例如“if 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。因此主代码可以通过赋值将名称注入到模块中。我正在尝试思考这是一个坏主意的原因。

I am writing a moderate-sized (a few KLOC) PyQt app. I started out writing it in nice modules for ease of comprehension but I am foundering on the rules of Python namespaces. At several points it is important to instantiate just one object of a class as a resource for other code.

For example: an object that represents Aspell attached as a subprocess, offering a check(word) method. Another example: the app features a single QTextEdit and other code needs to call on methods of this singular object, e.g. "if theEditWidget.document().isEmpty()..."

No matter where I instantiate such an object, it can only be referenced from code in that module and no other. So e.g. the code of the edit widget can't call on the Aspell gateway object unless the Aspell object is created in the same module. Fine except it is also needed from other modules.

In this question the bunch class is offered, but it seems to me a bunch has exactly the same problem: it's a unique object that can only be used in the module where it's created. Or am I completely missing the boat here?

OK suggested elsewhere, this seems like a simple answer to my problem. I just tested the following:

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

When I run junk_main it prints 99. So the main code can inject names into modules just by assignment. I am trying to think of reasons this is a bad idea.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

多孤肩上扛 2024-12-10 05:23:10

您可以使用 . 运算符访问模块中的对象,就像使用函数一样。因此,例如:

# 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__ 的列表,其中包含以下名称(作为字符串)列表您希望模块导出到 * 的所有对象。因此,如果您只想导出 EditWidget,您可以这样做:

# Module EditWidget.py
__all__ = ["theEditWidget"]
theEditWidget = EditWidget()
...

You can access objects in a module with the . operator just like with a function. So, for example:

# Module a.py
a = 3

>>> import a
>>> print a.a
3

This is a trivial example, but you might want to do something like:

# Module EditWidget.py
theEditWidget = EditWidget()
...

# Another module
import EditWidget

if EditWidget.theEditWidget.document().isEmpty():

Or...

import * from EditWidget

if theEditWidget.document().isEmpty():

If you do go the import * from route, you can even define a list named __all__ in your modules with a list of the names (as strings) of all the objects you want your module to export to *. So if you wanted only theEditWidget to be exported, you could do:

# Module EditWidget.py
__all__ = ["theEditWidget"]
theEditWidget = EditWidget()
...
红尘作伴 2024-12-10 05:23:10

事实证明答案比我想象的要简单。正如我在问题中指出的,主模块可以向导入的模块添加名称。任何代码都可以向对象添加成员。因此,创建模块间通信区域的简单方法是在主体中创建一个非常基本的对象,例如 IMC(用于模块间通信器)并将其分配为成员,以及其他模块可用的任何内容:

IMC.special = A.thingy()
IMC.important_global_constant = 0x0001

等等。导入任何模块后,只需为其分配 IMC:

import B
B.IMC = IMC

现在,从软件设计的角度来看,这可能不是最好的想法。如果您只是将 IMC 限制为保存命名常量,那么它的作用就像 C 头文件一样。如果只是为了提供对单一资源的访问,那么它就像外部链接。但由于Python的自由规则,任何模块中的代码都可以修改或添加IMC成员。如果以不规范的方式使用,“谁改变了它”可能会成为调试问题。如果有多个进程,竞争条件是一个危险。

It turns out the answer is simpler than I thought. As I noted in the question, the main module can add names to an imported module. And any code can add members to an object. So the simple way to create an inter-module communication area is to create a very basic object in the main, say IMC (for inter-module communicator) and assign to it as members, anything that should be available to other modules:

IMC.special = A.thingy()
IMC.important_global_constant = 0x0001

etc. After importing any module, just assign IMC to it:

import B
B.IMC = IMC

Now, this is probably not the greatest idea from a software design standpoint. If you just limit IMC to holding named constants, it acts like a C header file. If it's just to give access to singular resources, it's like a link extern. But because of Python's liberal rules, code in any module can modify or add members to IMC. Used in an undisciplined way, "who changed that" could be a debugging issue. If there are multiple processes, race conditions are a danger.

烟织青萝梦 2024-12-10 05:23:10

在某些时候,仅实例化类的一个对象作为其他代码的资源非常重要。

您是否可以不尝试创建某种单例工厂,而是在程序的主要入口点和实例化需要它的对象之间的某个位置创建一次性对象?一次性对象只能作为参数传递给另一个对象。从逻辑上讲,您不会多次创建一次性对象。

例如:

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 的网络服务器中,其中入口点几乎无处不在。

At several points it is important to instantiate just one object of a class as a resource for other code.

Instead of trying to create some sort of singleton factory, can you not create the single-use object somewhere between the main point of entry for the program and instantiating the object that needs it? The single-use object can just be passed as a parameter to the other object. Logically, then, you won't create the single-use object more than once.

For example:

def main(...):
    aspell_instance = ...
    myapp = MyAppClass(aspell_instance)

or...

class SomeWidget(...):

    def __init__(self, edit_widget):
        self.edit_widget = edit_widget

    def onSomeEvent(self, ...):
        if self.edit_widget.document().isEmpty():
            ....

I don't know if that's clear enough, or if it's applicable to your situation. But to be honest, the only time I've found I can't do this is in a CherryPy-based webserver, where the points of entry were pretty much everywhere.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文