Only One Shall Exist: The Singleton Pattern in Python

Only One Shall Exist: The Singleton Pattern in Python
Photo by call me hangry 🇫🇷 / Unsplash

What are Design Patterns Anyway

Design patterns help developers with repeating problems. They are like a blueprint for common problems.

To give an example from daily life, everytime you are about to tie your shoe laces, you don't think how to do it, someone came up with an efficient solution, a design pattern. That design pattern can be used not only for tying shoe laces, but also can be used for:

  • Tying a necktie — different knots (like Windsor or Half-Windsor) are design patterns for the same goal: securing a tie neatly.
  • Tying a garbage bag — you loop and knot it the same way every time without thinking.
  • Tying up a boat at a dock — sailors use specific knots (like a bowline or cleat hitch) depending on the situation — reliable “patterns” for securing lines.
  • Tying a gift ribbon or bow — same technique reused for different gifts; the design doesn’t change.

Singleton Design Pattern

Let's explore Singleton design pattern. The very first design pattern that's usually teached in courses. Let's dive right in..

A class can be instantiated many times, however what if we want to make sure it's instantiated only once. Singleton is instantiated only once and that object is returned(not instantiated) after the first time. So singleton makes sure, there is only one object instantiated from that class. Let's look at the diagram below to understand better:

The first created object is returned for every instantiation request

Which Problem does Singleton solve?

When there is a need to have one single object to be used in multiple places, like logging. We don't need to create a logger object everytime. We can create once and use that object wherever needed. For those use cases we don't need to create the object again and again but we can use the same object in different places. Other than a logger, singleton can also be used for:

  • Configuration objects
  • DB connection objects
  • Caching objects

Let's create a Singleton

Ok, now let's create a singleton in Python:

class Singleton:
    _instance = None

    def __init__(self):
        raise RuntimeError("Call get_instance() instead")

    @classmethod
    def get_instance(cls):
        if not cls._instance:
            cls._instance = cls.__new__(cls)

        return cls._instance


if __name__ == "__main__":
    s1 = Singleton.get_instance()
    s2 = Singleton.get_instance()
    print(s1 is s2)  # True
    print(f'id_s1={id(s1)}')  # id_s1=139662619872288
    print(f'id_s2={id(s2)}')  # id_s2=139662619872288

So, here we start by creating a class attribute called _instance . The underscore prefix denotes that this is a private attribute.

And then, we are disabling the instantiation(object creation) by raising an error in __init__ , and notifying users to use the class method called get_instance , which is the only way to instantiate the class.

get_instance class method instantiates the object and stores it in _instance class attribute, but only if it wasn't created before. If it was created before, it will simply return that object, which was stored in _instance class attribute.

Note that s1 and s2 has the same memory address, which means only one object was created, and s1 and s2 refer to the same object.

Another way to create a Singleton

There is another way to create a singleton, which doesn't require notifying users. We will use the __new__ method for this way. Let's look at the code below:

class Singleton:
    _instance = None

    def __new__(cls):
        if not cls._instance:
            cls._instance = super().__new__(cls)

        return cls._instance


if __name__ == '__main__':
    s1 = Singleton()
    s2 = Singleton()
    print(s1 is s2)  # True
    print(f'id_s1={id(s1)}')  # id_s1=140559185906720
    print(f'id_s2={id(s2)}')  # id_s1=140559185906720

In this example, we need to understand what the __new__ dunder method does.

__new__ is responsible for returning the new object (usually an instance of the class).

It's generally confused with the __init__ dunder method, however remember this:

__new__ is responsible for creating the object, __init__ is responsible for initializing the object.

Let's get back to the code. inside __new__ the code has the same logic as get_instance method that we saw in the previous example, which is, creating the instance only if it doesn't exist. If it does exist, it simply returns it.

This way, the logic for keeping only one object, is handled in the object creation phase(__new__ ).

Conclusion

Design patterns give structure and consistency to the way we solve common programming problems. The Singleton pattern, while simple, teaches an important concept — controlling object creation. By ensuring only one instance exists, it helps maintain a single source of truth for shared resources like configurations, loggers, or database connections. Whether implemented with a class method or by overriding __new__, understanding how it works deepens your grasp of Python’s object model — and sets the stage for exploring more advanced design patterns.