Fading Coder

One Final Commit for the Last Sprint

Home > Notes > Content

Python Singleton Pattern Implementation Approaches

Notes Sep 15 1

The Singleton Pattern ensures that a class has only one instance throughout the application lifecycle. This pattern proves useful when exactly one object is needed to coordinate actions across the system.

Module-Based Singleton

Python modules functon as natural singletons. When a module is first imported, Python generates a .pyc file. Subsequent imports load the cached bytecode instead of re-executing the module code. By encapsulating functions and data within a single module, you effectively create a singleton object.

# config.py
class Settings:
    def get_value(self, key):
        return f"value_{key}"

config = Settings()
from config import config

Decorator-Based Singleton

A decorator maintains a dictionary mapping classes to their instances. Upon first invocation, the decorator creates and stores the instance. Subsqeuent calls retrieve the cached instance instead of creating a new one.

def singleton(cls):
    _registry = {}

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

    return get_instance


@singleton
class DatabaseConnection:
    def __init__(self, host="localhost"):
        self.host = host
        print(f"Initializing connection to {host}")


conn1 = DatabaseConnection("192.168.1.1")
conn2 = DatabaseConnection("192.168.1.2")
print(id(conn1) == id(conn2))  # True

Output:

Initializing connection to 192.168.1.1
True

__new__ Method Singleton

The __new__ method controls instance creation, while __init__ handles initialization. By overriding __new__, you can intercept the instantiation process and return an existing instance if available.

import time

class AppState:
    _instance = None

    def __init__(self):
        self.timestamp = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def update_timestamp(self):
        self.timestamp = time.time()


if __name__ == "__main__":
    state1 = AppState()
    state2 = AppState()

    print(f"Same instance: {id(state1) == id(state2)}")

    state1.update_timestamp()
    print(f"State1 timestamp: {state1.timestamp}")
    print(f"State2 timestamp: {state2.timestamp}")

Output:

Same instance: True
State1 timestamp: 1593963540.9315283
State2 timestamp: 1593963540.9315283

Metaclass-Based Singleton

Metaclasses provide another elegant soluiton by controlling class creation itself.

class SingletonMeta(type):
    _instances = {}

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


class Logger(metaclass=SingletonMeta):
    def __init__(self):
        self.level = "INFO"

    def log(self, message):
        print(f"[{self.level}] {message}")


logger1 = Logger()
logger2 = Logger()
print(id(logger1) == id(logger2))  # True

Each approach offers distinct advantages: modules require no code changes, decorators provide flexibility, __new__ offers simplicity, and metaclasses deliver elegant reuse across multiple classes.

Tags: Python

Related Articles

Designing Alertmanager Templates for Prometheus Notifications

How to craft Alertmanager templates to format alert messages, improving clarity and presentation. Alertmanager uses Go’s text/template engine with additional helper functions. Alerting rules referenc...

Deploying a Maven Web Application to Tomcat 9 Using the Tomcat Manager

Tomcat 9 does not provide a dedicated Maven plugin. The Tomcat Manager interface, however, is backward-compatible, so the Tomcat 7 Maven Plugin can be used to deploy to Tomcat 9. This guide shows two...

Spring Boot MyBatis with Two MySQL DataSources Using Druid

Required dependencies application.properties: define two data sources and poooling Java configuration for both data sources MyBatis mappers for each data source Controller endpoints to verify both co...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.