Metaclass and Singleton
Introduction
As I promised, we are coming back with the second part of the tutorial related to singleton implementation in Python 3. Here we will cover two different approaches, much easier to understand and more Python-friendly. We will start with Metaclasses and in the last part, we will cover the singleton based on Python decorators.
Singleton and metaclasses
What is a metaclass? It’s actually the class of a class. To make a similarity just think: a class defines how its instance behaves, so a metaclass defines how a class behaves. A class is an instance of a metaclass. In this way, we can create classes of their own type from the predefined Python classes.
Implementation
class MetaClassSingleton(type):
"""
Meta class implementation
"""
_instances = {}
def __call__(cls, *args, **kwargs):
"""
Override __call__ special method based on singleton pattern
"""
if cls not in cls._instances:
cls._instances[cls] = super(MetaClassSingleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
class Driver(metaclass=MetaClassSingleton):
"""
Driver class decorated by the meta class: MetaClassSingleton.
Behaviour changed in singleton
"""
connection = None
def connect(self):
"""
Set the connection with the web driver
:return: web driver
"""
if self.connection is None:
self.connection = webdriver.Chrome()
return self.connection
Usage
# When we want to perform different interaction with a view, the driver class can be instantiated
# for multiple times, but only one object is created
dr1 = Driver().connect()
dr2 = Driver().connect()
print('Web driver object 1 ', dr1)
print('Web driver object 2', dr2)
Singleton based on ‘decorators’
A short intro about decorators and their usage. Actually, a decorator is the Python approach to change the behavior of a method. To do this, you just have to add an annotation before the definition of the method. The actual annotation for providing a singleton could be done by implementing first the decorator and afterward to integrate it. Below using the first function: my_singleton
we’ve implemented the singleton decorator so it could be used by the class MyDriver
The good part is that in Python you don’t even have to do this because there is an already existing package singleton_decorator
, so just to use it, like the classDriver
.
Implementation
from singleton_decorator import singleton
def my_singleton(*args):
"""
:param cls: class to be changed as a singleton
:return: instance of the singleton
"""
instances = dict()
def get_instance():
if args[0] not in instances:
instances[args[0]] = args[0]()
# else: #uncomment this part in order to raise a warning related to instantiation
# raise UserWarning("An instantiation already exists!")
return instances[args[0]]
return get_instance
@my_singleton
class MyDriver:
@staticmethod
def get_driver():
return webdriver.Chrome()
@singleton
class Driver:
@staticmethod
def get_driver():
return webdriver.Chrome()
Usage
For this usage scenario, we’ve implemented a unittest class, where we check if both objects are identical after the instantiation.
class TestDecoratorSingleton(TestCase):
def test_singleton(self):
dr1 = Driver()
dr1.get_driver().get('https://en.wikipedia.org/')
dr2 = Driver()
self.assertEqual(dr1, dr2)
def test_my_singleton(self):
dr1 = MyDriver()
dr1.get_driver().get('https://en.wikipedia.org/')
dr2 = MyDriver()
self.assertEqual(dr1, dr2)
Conclusion
This is it about singleton implementation, looking back using metaclasses or decorators, it’s for sure the clearest way of defining it, but if you don’t want to spend to much time thinking about it, just use the already existing package: singleton_decorator
. It’s also a best practice when developing something new in Python, look behind and maybe someone has already done this before and all you have to do is pip install ...
The source code of this article could be also found here
Nice post!! Tks guy
LikeLike
Thanks dude!
LikeLike