Background
It was several years ago that I have learned and used Design Patterns during my previous job. We developed software for medical robots in C++ (i.e OOP) and different design patterns were frequently used in our code. 
Later on, when I have started working in our startup, we develop a lot in Python and HTML5 for BigData & Web projects thus we use hardly design patterns.  
Recently I am reading this book about Design Patterns as shown below:

This book is very interesting and it makes me recall the knowledge on design patterns. I realize that it's time for me to do some design pattern exercises in Python.
Singleton exercise
So today I tried a simple scenario for implementing Singleton in Python. I want to simulate the case when an user calls siri to play a music item.
GO!
No Singleton Case
Define a regular player class without singleton implementation.
import threading
import time
## a global album dict containing three music as well as their length 
album = {"Love Story":10, "Angels":12, "Elle":18}
### a regular MusicPlayer class which contains a thread for simulating music playing
### 普通的播放器类,自带一个播放线程
class musicplayer():      
    """
        a regular MusicPlayer class 
    """
    def __init__(self):
        self.__currentItem = ""
        self.__musicLength = 0
        self.__timer = 0
        self.__thread = threading.Thread(target=self.playsound)
    def play(self):
        if  self.__thread.isAlive():
            pass
        else:
            self.__thread.start()
    def updateItem(self, musicfile, length):
        self.__currentItem = musicfile
        self.__musicLength = length
        self.__timer = 0
    def playsound(self):
        while self.__timer <= self.__musicLength:
            print(" -- playing   {}  ---- {}.00/{}.00" .format(self.__currentItem, self.__timer, self.__musicLength))
            time.sleep(1)
            self.__timer +=1
        print("{} playing thread has ended".format(self.__currentItem))
Now define a quite simple Siri class which has only one function runCMD for initializing player app and playing music.
class Siri():
    def __init__(self):
        pass
    def runCMD(self, cmd):
        if cmd.startswith("play music"):
            music = cmd.replace("play music", "").strip()
            player = musicplayer()
            player.updateItem(music, album[music])
            player.play()
Now try it in a main function.
if __name__ == "__main__":
    musicList = list(album.keys())
    siri = Siri()
    siri.runCMD("play music {}".format(musicList[0]))
    time.sleep(4)
    siri.runCMD("play music {}".format(musicList[1]))
    mainTime = 0
    while (mainTime < 20):
        mainTime+=1
        time.sleep(1)
    print("End of simulation")
Execute the code above, I see that two music players have been created and they played different items at the same time.
Singleton Case
Now I update a little bit the music player class so that it is a singleton-pattern class. This class is derived from object class so that I need to define __new__() and __init__() functions to do the constructor's work.
class musicplayer(object):      
    """
        ### a SINGLETON MusicPlayer class which contains a thread for simulating music playing
    """
    __instance = None
    __isInstanceDefined = False
    def __new__(cls):
        if not cls.__instance:
            musicplayer.__instance = super().__new__(cls)
        return cls.__instance
    def __init__(self):
        if not musicplayer.__isInstanceDefined:  
            self.__currentItem = ""
            self.__musicLength = 0
            self.__timer = 0
            self.__thread = threading.Thread(target=self.playsound)
            musicplayer.__isInstanceDefined = True
        else:
            pass
    def play(self):
        if  self.__thread.isAlive():
            pass
        else:
            self.__thread.start()
    def updateItem(self, musicfile, length):
        self.__currentItem = musicfile
        self.__musicLength = length
        self.__timer = 0
    def playsound(self):
        while self.__timer <= self.__musicLength:
            print(" -- playing   {}  ---- {}.00/{}.00" .format(self.__currentItem, self.__timer, self.__musicLength))
            time.sleep(1)
            self.__timer +=1
        print("{} playing thread has ended".format(self.__currentItem))
Now execute again the entire code file and it works. There is one and only one player instance in the simulation even siri changed the music item:
Singleton Decorator
In the book I read, the author introduced a very graceful way to realize Singleton pattern without modification of existing classes: using Decorator. I like this method very much.
This decorator method is explicit and simple:
def singletonDecorator(cls, *args, **kwargs):
    """ defines a Singleton decorator """
    instance = {}
    def wrapperSingleton(*args, **kwargs):
        if cls not in instance:
            instance[cls] = cls(*args, **kwargs)
        return instance[cls]
    return wrapperSingleton
Now I put my entire code file here. Note that there is @singletonDecorator declaration above my existing musicplayer class:
import threading
import time
album = {"Love Story":10, "Angels":12, "Elle":18}
def singletonDecorator(cls, *args, **kwargs):
    """ defines a Singleton decorator """
    instance = {}
    def wrapperSingleton(*args, **kwargs):
        if cls not in instance:
            instance[cls] = cls(*args, **kwargs)
        return instance[cls]
    return wrapperSingleton
### a regular MusicPlayer class which contains a thread for simulating music playing
### 普通的播放器类,自带一个播放线程
@singletonDecorator
class musicplayer():         
    """
        a regular MusicPlayer class 
    """
    def __init__(self):
        self.__currentItem = ""
        self.__musicLength = 0
        self.__timer = 0
        self.__thread = threading.Thread(target=self.playsound)
    def play(self):
        if  self.__thread.isAlive():
            pass
        else:
            self.__thread.start()
    def updateItem(self, musicfile, length):
        self.__currentItem = musicfile
        self.__musicLength = length
        self.__timer = 0
    def playsound(self):
        while self.__timer <= self.__musicLength:
            print(" -- playing   {}  ---- {}.00/{}.00" .format(self.__currentItem, self.__timer, self.__musicLength))
            time.sleep(1)
            self.__timer +=1
        print("{} playing thread has ended".format(self.__currentItem))
class Siri():
    def __init__(self):
        pass
    def runCMD(self, cmd):
        if cmd.startswith("play music"):
            music = cmd.replace("play music", "").strip()
            player = musicplayer() 
            player.updateItem(music, album[music])
            player.play()
if __name__ == "__main__":
    musicList = list(album.keys())
    siri = Siri()
    siri.runCMD("play music {}".format(musicList[0]))
    time.sleep(4)
    siri.runCMD("play music {}".format(musicList[1]))
    mainTime = 0
    while (mainTime < 20):
        mainTime+=1
        time.sleep(1)
    print("End of simulation")
 
 
              
 
    
Top comments (2)
The borg pattern is a Python staple: code.activestate.com/recipes/66531...
What's the name of the book?