What is the best way of implementing singleton in Python?

A single object can only be instantiated for a class thanks to the singleton design pattern. The Singleton pattern may be implemented in Python in some methods, each having pros and cons of its own. We’ll go over the many approaches to creating Singletons in Python, weigh the benefits and drawbacks of each, and give examples of each way in this in-depth tutorial.

A class is guaranteed to have a single instance according to the Singleton pattern, which also offers a global point of access to that instance. It is frequently used in situations (such as handling database connections, logging, and configuration settings) where just one instance of a class is needed for the duration of an application.

Using a Class Variable

Using a class variable to store one instance of the class is one of the easiest approaches to building Singleton in Python. Here’s an example:

class Singleton:

    _instance = None

    def __new__(cls):

        if cls._instance is None:

            cls._instance = super().__new__(cls)

        return cls._instance


Unique Through Decorator

PEP 318 — Decorator for Functions and Methods — introduced the decorator syntax to Python in 2003. In Python, a decorator is a function or method extension. It can therefore add functionality without changing the original expanded code at all.

All I have to do to construct one is create a function within a function that yields the single instance of a class. Next, use our Target class’s @singleton syntax decorator.

def singleton(class_):

   instances = {}

   def get_instance(*args, **kwargs):

       if class_ not in instances:

           instances[class_] = class_(*args, **kwargs)

       return instances[class_]

   return get_instance

@singleton

class Database:

   def __init__(self, url):

       self.url = url

   def connect(self):

       if 'https' not in self.url:

           raise ValueError("invalid url: it must be encrypted")

       return True


Singleton Via Metaclass

A unique method for automatically changing a class during creation is provided by Metaclass. It can define the behavior expected of that class.

To prevent creating an object of the Target class if one already exists, I can write a metaclass. The derived class building will be orchestrated by __call__, and __init__will only be called once.

class Singleton(type):

   _instances = {}

   def __call__(cls, *args, **kwargs):

       if cls not in cls._instances:

           cls._instances[cls] = super(type(cls), cls).__call__(*args, **kwargs)

       return cls._instances[cls]

class _Database():

   def __init__(self, url):

       self.url = url

   def connect(self):

       if 'https' not in self.url:

           raise ValueError("invalid url: it must be encrypted")

       return True

class DatabaseSingleton(_Database, metaclass=Singleton):

 pass


Using a Module

Since Python modules are only imported once per interpreter session, they are by default singletons. By putting the singleton logic in a module and importing it anywhere we want access to the singleton instance, we may benefit from this behavior.

# singleton.py

class Singleton:

    pass

singleton_instance = Singleton()


Using a Borg (Shared State) Pattern

Multiple instances of the same class can share the same state thanks to the Borg pattern, also called the Shared State pattern. Even if it doesn’t follow the Singleton pattern exactly, sharing state among instances allows for comparable outcomes.

class Borg:

    _shared_state = {}

    def __init__(self):

        self.__dict__ = self._shared_state

class Singleton(Borg):

    pass


Comparing the Approaches

Every Python Singleton implementation technique has benefits and drawbacks of its own. Several criteria, including ease of use, speed, and compatibility with the current codebase, influence the technique selection.

MethodProsCons
Class VariableEasy and uncomplicated executionNot thread-safe
Decoratorisolates the class definition from the singleton logicRequires modifying class definition
MetaclassesStrong and adaptable personalizationComplex syntax
ModuleUnintentional singleton generationrestricted to singletons without states
Borg Patternallows for the shared state between instancesrequires the Borg class to be inherited.

This article has several approaches to creating Singleton in Python, including utilizing decorators, metaclasses, class variables, modules, and the Borg pattern. Every approach has advantages and disadvantages of its own, and the best approach will rely on the particular needs of the application.

Consider learning more about the Singleton pattern and its uses in software design for more investigation. Moreover, investigate additional Python implementations of design patterns to deepen your grasp of software architecture and design concepts.

Share The Tutorial With Your Friends
Twiter
Facebook
LinkedIn
Email
WhatsApp
Skype
Reddit

Check Our Ebook for This Online Course

Advanced topics are covered in this ebook with many practical examples.