Singleton

P

pythoncurious

Hi,

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:

one/
__init__.py
mod1.py
run.py
two/
__init__.py
mod2.py

run.py looks like this:
#!/usr/bin/env python
import mod1
print mod1.number
import two.mod2
print two.mod2.number

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.

Looking at:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52558
I find a singleton class, but it has the same problem. If it's
imported in two different ways, I have not a singleton but a ...
'doubleton' or something.

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'm thankful for any advice you might provide.
 
C

Carl Banks

Hi,

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:

one/
__init__.py
mod1.py
run.py
two/
__init__.py
mod2.py

run.py looks like this:
#!/usr/bin/env python

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:

from . import mod1


See PEP 328.


[continuing again with run.py]
import two.mod2
print two.mod2.number

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".

Looking at:http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52558
I find a singleton class, but it has the same problem. If it's
imported in two different ways, I have not a singleton but a ...
'doubleton' or something.

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.



Carl Banks
 
O

OnCallSupport321

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!"

See also http://springpython.python-hosting.com/wiki/InversionOfControl
and http://springpython.python-hosting.com/wiki/DecoratorBasedApplicationContext.
 
H

Hrvoje Niksic

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.['UserDict', '__builtin__', '__main__', '_codecs', '_sre', '_types', 'codecs', 'copy_reg', 'encodings', 'encodings.aliases', 'encodings.codecs', 'encodings.encodings', 'encodings.types', 'encodings.utf_8', 'exceptions', 'linecache', 'one', 'one.mod1', 'one.mod2', 'os', 'os.path', 'posix', 'posixpath', 're', 'readline', 'rlcompleter', 'signal', 'site', 'sre_compile', 'sre_constants', 'sre_parse', 'stat', 'sys', 'types', 'warnings', 'zipimport']

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.
 
S

Steven D'Aprano

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?
 
D

Diez B. Roggisch

Steven said:
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.



class Borg(object):
__shared_state = {}

def __init__(self):
self.__dict__ = self.__shared_state


if __name__ == "__main__":
import test
mainBorg = Borg()
mainBorg.a = 100
testBorg = test.Borg()
testBorg.a = 200
print mainBorg.a
print testBorg.a



Run this as this:

droggisch@ganesha:/tmp$ ll *py
-rw-r--r-- 1 droggisch 298 2007-10-10 17:30 test.py
droggisch@ganesha:/tmp$ python test.py
100
200


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.

Diez
 
C

Carl Banks

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.


Carl Banks
 
S

Steven D'Aprano

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.
 
S

Steven D'Aprano

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.
 
P

pythoncurious

Hi again,

Just wanted to say thanks.
I now see that I misunderstood where the problem was and I got
useful information about how importing stuff works.

My problem is solved and I'm grateful for your help.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top