-3

I have a singleton.py, its code is bellow :

from functools import wraps

def singleton(cls):
    instances = {}

    @wraps(cls)
    def getinstance(*args, **kw):
        if cls not in instances:
            instances[cls] = cls(*args, **kw)
        return instances[cls]
    return getinstance

@singleton
class OTConn():
    def __init__(self):
        self.conn = None

And I have a test01.py:

from app04 import singleton

ot_conn = singleton.OTConn()

ot_conn.conn = "a"

And test02.py:

import singleton

ot_conn = singleton.OTConn()

print (ot_conn.conn)  # there print: None

I run the test01.pyand the test02.py in one process, but the ot_conn.conn prints None.


EDIT

I tried change the code to :

@singleton
    class OTConn():
        conn = None
        def __init__(self):
            pass

Still not work. Some friend can help me with the decorate method for singleton?


EDIT-2

See this singleton method

the decorator method is as same as mine.

244boy
  • 3,994
  • 8
  • 30
  • 72
  • Your singleton works only when called from the same process. If you are running two processes (one in PyCharm, one in console), you'll need to use some inter-process-communication (IPC) mechanism. – randomir Oct 10 '17 at 12:00
  • @randomir In one process still goes the None result. – 244boy Oct 10 '17 at 12:09

1 Answers1

1

It appears that you have not understood how to use the singleton decorator. You are working under the mistaken assumption that the class you decorate with singleton becomes an attribute of singleton. It does not.

Here is an example of how it works. Note that I have slightly modified the definition of singleton to remove some cruft. It only works in Python 3. If you need to use this decorator in Python 3, some of the cruft will have to be put back.

from functools import wraps

def singleton(cls):
    instance = None
    @wraps(cls)
    def getinstance(*args, **kwds):
        nonlocal instance
        if instance is None:
            instance = cls(*args, **kwds)
        return instance
    return getinstance

# Check that it works ... first use the decorator on a class definition
@singleton
class Foo:
    pass

# Now instantiate the class twice ...
a = Foo()
b = Foo()

# ... and verify that we have only one instance.
assert a is b

Python's is operator verifies object identity. the assert a is b confirms that a and b are the same single instance.

Here is how verify that it works in a way which resembles a bit more what you tried to do in your test code:

# Showing how it works with your example
@singleton
class OTConn:
    # Your class variable `conn` and constructor were completely
    # irrelevant, so I leave them out
    pass

ot_conn_a = OTConn()
ot_conn_a.conn = 'a'

ot_conn_b = OTConn()
assert ot_conn_b.conn == 'a'

The most important difference between your code and mine, is that singleton.OTConn does not appear anywhere in mine. It cannot possbily work! (Unless there is some code which you haven't shown us, which makes it work.)

Step-by-step instrctions how to observe singleton functioning correctly

The behaviour you report, and your continued refusal to describe exactly how it is that you think you are executing this code in a single process, gives the very strong impression that you are executing this in separate processes. As has been pointed out to you by many people, your singleton class cannot possibly work in that case.

Here I give you a step-by-step recipe how to observe singleton working correctly in multiple modules but a single process.

Step 1: create a directory (folder) in which you will create the following python source files.

Step 2: In the directory you created in step 1 create a file called singleton.py containing the following code:

def singleton(cls):
    instance = None
    @wraps(cls)
    def getinstance(*args, **kwds):
        nonlocal instance
        if instance is None:
            instance = cls(*args, **kwds)
        return instance
    return getinstance

Step 3: In the same directory create a file called theclass.py containing the code

from singleton import singleton

@singleton
class TheClass:

    def __init__(self):
        self.conn = None

Step 4: Again, in the same directory create a file called client1.py containing

from theclass import TheClass

instance = TheClass()
print("In client1.py instance.conn =", instance.conn)
instance.conn = 'XXXX'
print("In client1.py instance.conn =", instance.conn)

Step 5: ... and also a file called client2.py containing

from theclass import TheClass

instance = TheClass()
print("In client2.py instance.conn =", instance.conn)

Step 6: The last file you need should be called main.py and contain

from client1 import instance as i1
from client2 import instance as i2

print("In main.py i1.conn =", i1.conn)
print("In main.py i2.conn =", i2.conn)

i2.conn = 'YYY'

print("In main.py i1.conn =", i1.conn)
print("In main.py i2.conn =", i2.conn)

Step 7: Execute main.py

Step 8: observe the output

In client1.py instance.conn = None
In client1.py instance.conn = XXXX
In client2.py instance.conn = XXXX
In main.py i1.conn = XXXX
In main.py i2.conn = XXXX
In main.py i1.conn = YYY
In main.py i2.conn = YYY

As you can see from the output

  • TheClass is instantiated in two separate modules.
  • the conn attribute of the instances starts of as None
  • Setting this attribute's value to 'XXXX' sets it for both instances.
  • Setting this attribute's value to 'YYY', this time using the other instance to do so, once again changes both instances.

The unavoidable conclusion is that singleton works as advertised. But this does crucially rely on both instances being in the same process.

jacg
  • 1,728
  • 10
  • 21
  • There are all, I maybe use the wrong code to do the decorator method for singleton. and if in one process, it still not work as singleton. – 244boy Oct 10 '17 at 12:15
  • there is no AttributeError on my process. – 244boy Oct 10 '17 at 12:17
  • @244boy If I paste your 3 code samples into the same file, remove the two imports, and apply the change that appears in your edit, and the execute the whole file, I get an `AttributeError` on the first line that says `ot_conn = singleton.OTConn()`. This is what I expect to happen, becasue **nowhere in the code that you have shown, does `singleton` acquire a `OTConn` attribute** – jacg Oct 10 '17 at 12:21
  • See my edit-2, my friend. you will see my method should be correct. – 244boy Oct 10 '17 at 12:26
  • Nowhere in the post that you link in your second edit, is there a suggestion that the class you are decorating appears as an **attribute of `singleton`**! I repeat: I have verified my theoretical analysis with experiment: the code you have posted gives an `AttributeError` as soon as you say `singleton.OTConn`. Even after your edit. – jacg Oct 10 '17 at 12:31
  • @244boy I have edited my answer to show you how to use `singleton`. I have also cleaned up its implementation a little bit. – jacg Oct 10 '17 at 12:46
  • I saw your edited answer, and my situation is, many .py files in my actual project, will import the singleton.py, and I set the singleton shared variate to (such as) "a" , if I take out the shared variate in other py file, it is None, I want the set "a". – 244boy Oct 10 '17 at 15:09
  • @244boy I have edited the answer to give detailed, step-by-step instructions indicating how to observe `singleton` behaving as it is supposed to. – jacg Oct 10 '17 at 16:08
  • Thank you, I get it. my mistake is combine the singleton class and the decorate method to a single py file. – 244boy Oct 11 '17 at 04:03
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/156414/discussion-between-jacg-and-244boy). – jacg Oct 11 '17 at 05:20