Singletons in Python
Once upon a time, two partners were owning a company and each of them has a notebook to track the expenses and the income of the company.
One day, they were calculating the revenue that the company achieved during that year, and suddenly they figured out that the numbers do not match and there were some missing inputs in both notebooks.
Started to think about a solution and while they were in the kitchen drinking coffee and discussing the issue, a programmer heard their discussion and shouted loudly
“Use a Singelton!”
A Singleton is a creational design pattern ensuring a class can have only one instance by hiding/manipulating the constructor and exposing an additional method that returns an object of that class and maintains its oneness.
How to write a Singleton in Python?
There are different ways to implement a Singleton in Python. I will present the most three used ones.
Modify the __new__ class method:
Using this approach, all that we have to do is to modify the __new__ method that is used for creating a new class object and give it the responsibility for ensuring the instance oneness.
class TestSingletoneClass(object):
#Holder for the Singleton instance
_instance = None
def __new__(cls):
#Make sure that one instance is created
if not cls._instance :
cls._instance = super(TestSingletoneClass, cls).__new__(cls)
return cls._instance
def __init__(self):
#define class attributes
self.x = 1
self.y = 2
instance1 = TestSingletone() #Create instance
instance2 = TestSingletone() #Get the already created instance
instance1 is instance2 #True
print(instance1.x,instance2.x) #(1,1)
#Trying to modify the internal state of the Singleton
instance2.x+=3
print(instance1.x,instance2.x) #(4,4)
Using the Metaclass approach:
With this approach, we define a metaclass that implements the Singleton logic.
class Singleton(type):
#Contains the instances of the different classes
_instances={}
def __call__(cls,*args,**kwargs):
"""
This function will be called on the class is called
(new instance is created).
"""
instance = cls._instances.get(cls)
if not instance:
instance = super().__call__(*args, **kwargs)
cls._instances[cls]=instance
return instance
class TestSingletoneClass(metaclass =Singleton):
def __init__(self):
self.x = 1
self.y = 2
def print_hello(self):
return "HelloWrold"
instance1 = TestSingletone() #Create instance
instance2 = TestSingletone() #Get the already created instance
instance1 is instance2 #True
print(instance1.x,instance2.x) #(1,1)
#Trying to modify the internal state of the Singleton
instance2.x+=3
print(instance1.x,instance2.x) #(4,4)
Using the decorator approach:
we define a decorator that handles the creation of class instances.
class Singleton:
def __init__(self, decorated_class):
self.decorated_class = decorated_class
self._instance = None#Holds the class instance
def get_instance(self):
"""
Returns class instance. This function will be used instead
of calling the __new__ method of the decorated class.
"""
if not self._instance:
self._instance = self.decorated_class()
return self._instance
def __call__(self):
"""
Disable the class calls. When there is a try to create
an instance just calling the class, we block it and rais an error
"""
raise TypeError('Instances should be created using ""get_instance()".')
@Singleton
class TestSingletoneClass:
def __init__(self):
self.x = 1
print ('Instance was initialized')
type_error_is_returned = TestSingletoneClass() #TypeError:Instances should be created using ""get_instance()".
instance1 = TestSingletoneClass.get_instance() #Create instance
instance2 = TestSingletoneClass.get_instance() #Get the already created instance
instance1 is instance2 #True
print(instance1.x,instance2.x) #(1,1)
#Trying to modify the internal state of the Singleton
instance2.x+=3
print(instance1.x,instance2.x) #(4,4)
Where/When to use Singletons:
There are many possible applications for the singleton design pattern:
- Maintain application global state
- The application Logger to avoid the issues caused by concurrent logging.
- Implementing a Cache.
- Application configurations.
- … etc.