开发者

Python namespaces: How to make unique objects accessible in other modules?

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 t开发者_如何转开发o 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.


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()
...


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.


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜