Dorian Amouroux
Dorian Amouroux

Reputation: 157

Create singleton Queue Python

I'm creating a bunch of components in my project, each of them will be running in different threads.
They will communicate through queue.Queue. I would like to define all the different queues, which is classes that extends queue.Queue. Each of them will be singleton object, so the different components can just import that class, and get the instance.

I put everything in one file:

from queue import Queue

class QueueAudio(Queue):

    def __new__(cls, *args, **kwargs):
        """
        Singleton Queue
        """
        if '_inst' not in vars(cls):
            cls._inst = object.__new__(cls, *args, **kwargs)
            print('Creating {}'.format(cls.__name__))
        return cls._inst

class AudioInput(object):

    def __init__(self):
        self.queue = QueueAudio()
        print(self.queue)

    def run(self):
        self.queue.put('Input Audio')


class Interpretor(object):

    def __init__(self):
        self.queue = QueueAudio()
        print(self.queue)

    def run(self):
        print(self.queue.get())

audio = AudioInput()
audio.run()
interpretor = Interpretor()
interpretor.run()

In the constructor of the two components (AudioInput and Interpretor) I print the object, to make sure they are the same. AudioInput put a string in the queue, and Interpretor reads it. However, the queue in Interpretor is always empty, making the program hangs forever. Here's the output of the program:

Creating QueueAudio
<__main__.QueueAudio object at 0x103234c18>
<__main__.QueueAudio object at 0x103234c18>

Upvotes: 1

Views: 1712

Answers (3)

Manouchehr Rasouli
Manouchehr Rasouli

Reputation: 560

hi you can implement your singleton queue as this example

create a singleton decorator

# singleton.py
def singleton(class_):
     instances = {}

     def getinstance(*args, **kwargs):
         if class_ not in instances:
             instances[class_] = class_(*args, **kwargs)
         return instances[class_]

     return getinstance

know define your queue class with singleton decoration

 #singleton_queue.py
 import singleton.py
 import queue


 @singleton
 class MySingletonQueue:

      def __init__(self, **kwargs):
           self.q = queue.Queue()

      def put(self, data):
           self.q.put(data)
      ...

i hope that is helpful

Upvotes: 0

Aran-Fey
Aran-Fey

Reputation: 43316

What's happening here is that after your __new__ function returns the queue object, python implicitly calls __init__ on it. This resets the queue and any items you've previously added are removed.

From the docs on __new__:

If __new__() returns an instance of cls, then the new instance’s __init__() method will be invoked [...]

Step by step, this is what happens:

audio = AudioInput()        # creates an empty queue
audio.run()                 # adds a value to the queue
interpretor = Interpretor() # implicit call to Queue.__init__ resets queue
interpretor.run()           # queue is empty, call blocks

This is a common problem when implementing singletons with __new__. You can work around this by using a metaclass, or by using a different function to get the queue singleton:

class QueueAudio(Queue):
    @classmethod
    def new(cls):
        if '_inst' not in vars(cls):
            cls._inst = cls()
            print('Creating {}'.format(cls.__name__))
        return cls._inst

# create a queue instance with "QueueAudio.new()"

Upvotes: 2

Dorian Amouroux
Dorian Amouroux

Reputation: 157

I solve my problem by using another method to define my class as a singleton :

class Singleton(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]

class QueueAudio(Queue, metaclass=Singleton):
    pass

Upvotes: 2

Related Questions