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?