I've been trying to get some sort of singleton working in python, but I struggle a bit and I thought I'd ask for advice.
The first approach was simply to use a module, and every variable in it will be seen by all who import the module. That works in some cases, but not if I have the following structure:
mod1.py looks like this: import random number=random.randint(0,100)
mod2.py looks like this import one.mod1 number = one.mod1.number
PYTHONPATH is set to the directory containing the 'one' and 'two' directories.
Now when I run the 'run.py', it will print two different numbers. sys.modules tells me that 'mod1' is imported as both 'one.mod1' and 'mod1', which explains the result.
It is possible to solve this by always importing with the complete path like 'one.mod1', even when inside the 'one' directory, but that's an error waiting to happen.
So how do people solve this? Is there an obvious way that I missed?
> I've been trying to get some sort of singleton working in python, but > I struggle a bit and I thought I'd ask for advice.
> The first approach was simply to use a module, and every variable in > it will be seen by all who import the module. > That works in some cases, but not if I have the following structure:
Well, here's your problem. Your main script is inside a package: that is what's causing the namespace difficulties you're seeing.
I highly suggest you not do that. A better ideas is to create a small script in the project root that imports run.py, like this:
#!/usr/bin/env python from one import run
[continuing with run.py]
> import mod1 > print mod1.number
It's is only tangentially related to your problem, but this usage is deprecated.
Importing a file in the same package using "import modl" instead of "from one import mod1" is called an impicit relative import (well, actually in this case it isn't a relative import--more on that later-- but you seem to have intended it as a relative import). Implicit relative imports are scheduled to be removed from a future release of Python.
You should use either an absolute import:
from one import mod1
Or an explicit relative import in Python 2.5 or above:
> mod1.py looks like this: > import random > number=random.randint(0,100)
> mod2.py looks like this > import one.mod1 > number = one.mod1.number
> PYTHONPATH is set to the directory containing the 'one' and 'two' > directories.
> Now when I run the 'run.py', it will print two different numbers. > sys.modules tells me that 'mod1' is imported as both 'one.mod1' and > 'mod1', which explains the result.
Here's the problem: the main script is NOT executed within the package "one". A file's presence in a package directory is not sufficient for it to be part of the package: you actuatlly have to import the file through that package.
When you ran run.py, the Python interpreter did not import the file via the "one" package. It loaded the file, compiled it, and ran it, but as a standalone file, not as part of a package.
Then, when you imported "mod1" from "run.py", it only saw that mod1.py was a file in the same directory. So it imported it as a regular module, not a submodule of a package.
Now, when mod2 imported mod1, it went through the package "one" to import it. Therefore, it considered mod1 to be part of the package "one", and so imported it as a submodule of "one".
> It is possible to solve this by always importing with the complete > path like 'one.mod1', even when inside the 'one' directory, but that's > an error waiting to happen.
I've been doing it for a few years; I haven't had a problem. If you're concerned, you can add "from __future__ import absolute_imports" to your files to catch any lapses.
> So how do people solve this? Is there an obvious way that I missed?
> I'm thankful for any advice you might provide.
Just keep your main scripts out of the package directories and you'll be fine.
Spring Python (http://springpython.python-hosting.com) offers a singleton solution. You can mark up function calls to only be called the first time, i.e. as singletons. The results are cached inside the Spring Python IoC container, so the next time the function is called, it returns the cached copy. Prototypes bypass the caching, and instead allow the function to be called everytime. This also support the idea of prototype functions contain references to singleton objects.
from springpython.context import *
class MyContainer(DecoratorBasedApplicationContext): def __init__(self): DecoratorBasedAppicationContext.__init__(self)
@component(scope.SINGLETON) # You can leave off the argument, since scope.SINGLETON is the default) def singletonComponent(self): return ["There can be only one!"]
@component(scope.PROTOTYPE): def prototypeComponent(self): return ["I think I'm a clone now. There's another one of me always hangin' around!"]
if __name__ == "__main__": appContext = MyContainer()
obj1 = appContext.singletonComponent() obj2 = appContext.singletonComponent() if obj1 == obj2: print "These are the same object" else: print "Something is wrong!"
obj3 = appContext.prototypeComponent() obj4 = appContext.prototypeComponent() if obj1 != obj2: print "These are different instances of the function call." else: print "These shouldn't be the same!"
On Oct 10, 9:39 am, Steven D'Aprano <st...@REMOVE-THIS-
cybersource.com.au> wrote: > On Wed, 10 Oct 2007 09:36:56 +0000, pythoncurious wrote: > > So how do people solve this? Is there an obvious way that I missed?
> Mostly by avoiding singletons. Why do you need only one instance? Perhaps > you should consider the Borg pattern instead.
That wouldn't help. In the OP's case, it would create two borgs. Did anyone actually read the OP's post? Or is this just an opportunity to pimp libraries and bicker about style?
pythoncuri...@gmail.com writes: > Now when I run the 'run.py', it will print two different numbers. > sys.modules tells me that 'mod1' is imported as both 'one.mod1' and > 'mod1', which explains the result.
If I were you, I'd make sure that the module duplicate problem is resolved first, for example by putting run.py somewhere outside one/. Then the singleton problem disappears as well.
> It is possible to solve this by always importing with the complete > path like 'one.mod1', even when inside the 'one' directory, but > that's an error waiting to happen.
Is it, really? As far as I can tell, Python handles that case rather robustly. For example:
$ mkdir one $ touch one/__init__.py $ touch one/mod1.py one/mod2.py $ echo 'import mod2' > one/mod1.py $ python Python 2.5.1 (r251:54863, May 2 2007, 16:56:35) [GCC 4.1.2 (Ubuntu 4.1.2-0ubuntu4)] on linux2 Type "help", "copyright", "credits" or "license" for more information.
Although mod1 imports mod2 simply with "import mod2", the fact that mod1 itself is imported as part of "one" is respected. As a result, mod2 is imported as "one.mod2", exactly as if it were imported from outside the "one" package.
run.py is an exception because it is started directly using "python run.py", so it never gets the information that it's supposed to be part of a package. To fix the problem, all you need to do is make sure that executable scripts such as run.py are either placed safely outside the package, or that they take care to always use absolute imports, such as "import one.mod1" instead of "import mod1". Placing them outside the package is a good example of preventing an error waiting to happen, like the one you hinted at.
On Wed, 10 Oct 2007 06:57:15 -0700, Carl Banks wrote: > On Oct 10, 9:39 am, Steven D'Aprano <st...@REMOVE-THIS- > cybersource.com.au> wrote: >> On Wed, 10 Oct 2007 09:36:56 +0000, pythoncurious wrote: >> > So how do people solve this? Is there an obvious way that I missed?
>> Mostly by avoiding singletons. Why do you need only one instance? >> Perhaps you should consider the Borg pattern instead.
> That wouldn't help. In the OP's case, it would create two borgs.
Two borgs, or two million, the whole point of using borgs is that it doesn't matter.
> Did > anyone actually read the OP's post? Or is this just an opportunity to > pimp libraries and bicker about style?
Well, let's see now... the OP says:
"I've been trying to get some sort of singleton working in python, but I struggle a bit and I thought I'd ask for advice."
If we take each word to have it's normal English meaning, then the OP is struggling to get a singleton working correctly in Python. The way I solve that problem is to deftly side-step it by not using a singleton.
("Doctor, it hurts when I do this." "Then don't do it.")
Of course, if there is some other meaning to the OP's post, then possibly I've completely misunderstood it. Would you mind telling me what you've seen that the rest of us haven't?
Steven D'Aprano wrote: > On Wed, 10 Oct 2007 06:57:15 -0700, Carl Banks wrote:
>> On Oct 10, 9:39 am, Steven D'Aprano <st...@REMOVE-THIS- >> cybersource.com.au> wrote: >>> On Wed, 10 Oct 2007 09:36:56 +0000, pythoncurious wrote: >>> > So how do people solve this? Is there an obvious way that I missed?
>>> Mostly by avoiding singletons. Why do you need only one instance? >>> Perhaps you should consider the Borg pattern instead.
You will see that there are _two_ different Borg-Cubes, as the output indicates. This has hit me more than once, and Carl Banks pointed that error out to the OP.
And if you'd follow your own advice of " take each word to have it's normal English meaning," then the OP is not
"struggling to get a singleton working"
but struggling to get "some sort of singleton working", as you cite yourself, and first tried to implement his needs using no singleton-recipe (or borg pattern) but a module:
""" The first approach was simply to use a module, and every variable in it will be seen by all who import the module. That works in some cases, but not if I have the following structure: """
Which didn't work out for the same reason his singleton approach didn't work and your beloved Borg-pattern doesn't as well.
On Oct 10, 11:18 am, Steven D'Aprano <st...@REMOVE-THIS-
cybersource.com.au> wrote: > Of course, if there is some other meaning to the OP's post, then possibly > I've completely misunderstood it. Would you mind telling me what you've > seen that the rest of us haven't?
I read far enough to see that OP had mischaracterized the problem and that it wasn't a problem with singletons at all; it was a problem with importing.
And that it would still manifiest itself with borgs.
On Wed, 10 Oct 2007 09:52:26 -0700, Carl Banks wrote: > On Oct 10, 11:18 am, Steven D'Aprano <st...@REMOVE-THIS- > cybersource.com.au> wrote: >> Of course, if there is some other meaning to the OP's post, then >> possibly I've completely misunderstood it. Would you mind telling me >> what you've seen that the rest of us haven't?
> I read far enough to see that OP had mischaracterized the problem and > that it wasn't a problem with singletons at all; it was a problem with > importing. And that it would still manifiest itself with borgs.
Fair enough, thanks to Diez's example, I see that now.
The way I read the OP's post, he was only having problems *because* of the requirement "I must have a singleton". Change the requirement to "I don't need a singleton" and maybe the problem goes away, yes? And if not, then at least it becomes obvious that the problem is nothing to do with singletons.
On Wed, 10 Oct 2007 17:37:53 +0200, Diez B. Roggisch wrote: >> Two borgs, or two million, the whole point of using borgs is that it >> doesn't matter.
> It does matter where the classes live (module-wise). Which is the > problem the OP had, borg or not to borg.
Gotcha. Thanks for the demonstration code, that makes it clear.
[snip]
> You will see that there are _two_ different Borg-Cubes, as the output > indicates. This has hit me more than once, and Carl Banks pointed that > error out to the OP.
> And if you'd follow your own advice of " take each word to have it's > normal English meaning," then the OP is not
> "struggling to get a singleton working"
> but struggling to get "some sort of singleton working", as you cite > yourself, and first tried to implement his needs using no > singleton-recipe (or borg pattern) but a module:
Using modules *is* a recipe for getting singletons, as the OP clearly understood.
> Which didn't work out for the same reason his singleton approach didn't > work and your beloved Borg-pattern doesn't as well.
It's not my beloved Borg-pattern. My original post asked: "Why do you need only one instance?" and suggested that *perhaps* he should *consider* an alternative. Sheesh. It's not like I said that the Borg solves every problem every time.