Skip to main content
Python

Singletons in Python

8 mins

A single king sitting on a throne, representing the idea that only one leader (instance) governs and coordinates actions across the entire kingdom (system)

What is a Singleton? #

A singleton is a design pattern that restricts the instantiation of a class to one single instance. This is useful when exactly one object is needed to coordinate actions across the system.

Though some developers argue that singletons can lead to code that is difficult to test and maintain, and even consider it an anti-pattern, they can be useful in certain scenarios.

Some common use cases for singletons include:

  • Managing a connection pool
  • Configuration management
  • Thread pools
  • Caching
  • Resource management
  • Application-wide settings

Python does not have built-in support for singletons, but there are several ways to implement them, each with its own pros and cons.

New Method Singleton #

Fits the classical implementation of the singleton pattern

The new method singleton is probably the most common way to implement a singleton in Python, and is the most familiar to developers coming from other languages.

class NewMethodSingleton:
    _instance = None
    _initialized = False

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self):
        # Initialize only once
        if not self.__class__._initialized:
            self.initialized = True
            self.data = []

# Usage
s1 = NewMethodSingleton()
s2 = NewMethodSingleton()
print(s1 is s2)  # True - same instance

print(id(s1), id(s2)) # same id

The __new__ method is a special method in Python that is responsible for creating a new instance of a class and is called before __init__, which initializes the instance.

The cls._instance variable is a class variable that holds the single instance of the class. So every call to NewMethodSingleton() will return the same instance as per the singleton pattern.

Note that the __init__ method initializes the single variables only once. Without this, the following would occur.

s1 = NewMethodSingleton()
s1.data.append(1)

print(s1.data)  # [1]

s2 = NewMethodSingleton()
print(s2.data)  # [] breaks the singleton pattern

Why Use the New Method Singleton? #

Useful when portability is a concern, as it is similar to the singleton pattern in other languages like Java and C#. The class itself implements the singleton pattern, so quick to understand what the class does immediately. When dealing with a large codebases, this can be helpful.

Decorator Method Singleton #

A more Pythonic way to implement a singleton

The decorator method is a more Pythonic way to implement a singleton. It uses a decorator function to wrap the class and ensure that only one instance is created.

The decorator function below, uses a dictionary to store instances of the classes it decorates. It is expecting a class as an argument, so the decorator should be applied to a class definition.

def singleton(cls):
    instances = {}

    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]

    return get_instance

To make any class a singleton, simply apply the @singleton decorator to the class definition. The decorator will ensure that only one instance of the class is created, regardless of how many times it is instantiated.

@singleton
class DecoratorMethodSingleton:
    def __init__(self):
        self.data = []
    
    def add_data(self, value):
        self.data.append(value)
    
    def get_data(self):
        return self.data

# Usage
s1 = DecoratorMethodSingleton()
s1.add_data(1)
print(s1.get_data())  # [1]

s2 = DecoratorMethodSingleton()
print(s2.get_data())  # [1] - same value

print(id(s1), id(s2)) # same id

Why Use the Decorator Method Singleton? #

Any class can be made into a singleton by simply applying the @singleton decorator. But more of a matter of preference, as the decorators may not be familiar to all developers. The implementation of using a dictionary to store instance is common in other singleton implementations.

Drawbacks of the Decorator Method #

By annotating a class with the @singleton decorator, effectively makes the class appear as function. For example, the __name__ attribute of the class will be the name of the decorator function, not the class itself, and the __isinstance__ check will fail, and can cause confusion if not aware of this.

For example, the following code:

print(DecoratorMethodSingleton.__name__)
res = isinstance(first_singleton, DecoratorMethodSingleton)
print(res)

will output (Python 3.12):

get_instance
Traceback (most recent call last):
  File "/programmerpulse/decoratorsingleton.py", line 34, in <module>
    res = isinstance(first_singleton, DecoratorMethodSingleton)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: isinstance() arg 2 must be a type, a tuple of types, or a union

Metaclass Method Singleton #

A more advanced way to implement a singleton

The metaclass method is a more advanced way to implement a singleton, but does require a deeper understanding of Python’s object-oriented programming. It uses a metaclass to control the instantiation of the class. A metaclass is a class of a class that defines how a class behaves.

Similar to the decorator method, the metaclass method uses a dictionary to store instances of the classes it creates.

The key part of the implementation is the __call__ method, which is overridden in the metaclass. The __call__ method is invoked whenever an instance of a class is created.

class SingletonMeta(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            instance = super().__call__(*args, **kwargs)
            cls._instances[cls] = instance
        return cls._instances[cls]


class MetaclassMethodSingleton(metaclass=SingletonMeta):
    def __init__(self):
        self.data = []
    
    def add_data(self, value):
        self.data.append(value)
    
    def get_data(self):
        return self.data


# Usage
s1 = MetaclassMethodSingleton()
s1.add_data(1)
print(s1.get_data())  # [1]
s2 = MetaclassMethodSingleton()
print(s2.get_data())  # [1]

print(id(s1), id(s2))  # same id```

Why Use the Metaclass Method Singleton? #

A good choise for a framework or library especially with hierarchical classes, where you want to enforce the singleton pattern across all subclasses.

Module Method Singleton #

A quick and easy way to implement a singleton

The module method singleton takes advantage of a feature of importing modules in Python. When a module is imported, it is only loaded once, and subsequent imports return the same module object. This means that you can use a module as a singleton by simply defining your singleton class in a module.

# config.py
data = []

def add_item(item):
    data.append(item)

def get_data():
    return data


# another_file.py
import config as cfg

def print_data():
    print("items are", cfg.get_data())


# main.py
import config
import another_file

config.add_item("abd")

another_file.print_data() # outputs items are 'abd']

Use Case #

The module method is the simplest way to implement a singleton in Python. It is easy to understand and does not require any special syntax or decorators.

However, it does requires developers to understand the module import system in Python, which may not be familiar to developers coming from other languages. Also, note that the singleton is not instantiated lazily, which may not be desired if it depends on resources being setup be instantiation.

Also, unlike the other singleton methods, there is no class involved. So no inheritance or polymorphism is possible. It is simply a module with functions and variables that can be accessed from anywhere in the codebase.

The module method is a good choice for configuration management, logging as no complexity is needed.

Borg Method Singleton #

For those that kind of like the singleton pattern, but not really

The Borg method is a variation of the singleton pattern that allows multiple instances to share the same state.

It does this by using a shared dictionary that is stored in the class itself. Each instance of the class accesses the same dictionary, so they all share the same state.

class Borg:
    _shared_state = {}

    def __new__(cls, *args, **kwargs):
        obj = super().__new__(cls)
        obj.__dict__ = cls._shared_state
        return obj


class BorgMethodSingleton(Borg):
    def __init__(self):
        if not hasattr(self, 'initialized'):
            self.initialized = True
            self.data = []

    def add_data(self, value):
        self.data.append(value)

    def get_data(self):
        return self.data


# Usage
s1 = BorgMethodSingleton()
s1.add_data(1)
print(s1.get_data())  # [1]
s2 = BorgMethodSingleton()
print(s2.get_data())

print(id(s1), id(s2)) # different ids
print(id(s1.__dict__), id(s2.__dict__)) # same id

The main advantage of this method is that it allows for more flexibility than the traditional singleton pattern, but it can also lead to confusion if not used carefully. It is important to understand the implications of sharing state between instances before using this method.

Why Use the Borg Method Singleton? #

More flexible than the traditional singleton pattern, due to the shared state. One use case maybe where there are multiple singletons required for the same resource. e.g. a printer singleton, where there singleton instance represent variations, e.g. Black and White, Color, etc, but share the same printer resource state.

Why is it called the Borg method? #

The name Borg comes from Star Trek — specifically the Borg Collective, a race of cybernetic organisms that share a hive mind.

Their tagline is famous:

“You will be assimilated. Resistance is futile.”

In the show:

  • Each Borg has its own body (like an object instance)

  • But all Borg share the same consciousness (like shared state via dict)

Pros and Cons of Each Method #

In summary, here are the pros and cons of each method:

Pattern Pros Cons When to Use
__new__ method - Fine-grained control over instantiation
- Pure Python (no decorators/metaclasses)
- May confuse readers unfamiliar with __new__
- Needs care with __init__ (runs every time)
When you want tight control and lightweight logic
Decorator method - Very reusable
- Simple and readable
- Easy to apply to any class
- The decorated class becomes a function (loses __name__, __doc__, etc.) unless wrapped properly When you want a quick Singleton wrapper across many classes
Metaclass method - Elegant and automatic
- Doesn’t alter your class usage
- Can enforce Singleton for entire hierarchies
- Less readable to beginners
- Metaclasses can be overkill/confusing
When building frameworks or libraries where you want Singleton logic built-in
Module method - Easiest and most Pythonic
- Modules are already singletons by design
- No class involved (can’t subclass or instantiate)
- Limited to simple use cases
When you just need a config, logger, or state holder — nothing fancy
Borg method - Allows multiple instances that share state
- More flexible than classic Singleton
- Counterintuitive: a is not b, but a.__dict__ == b.__dict__
- Can lead to subtle bugs if misunderstood
When you want “shared-state” behavior but not strict instance uniqueness