Fading Coder

One Final Commit for the Last Sprint

Home > Notes > Content

Python Singleton Pattern Implementation Approaches

Notes Sep 15 46

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...

Skipping Errors in MySQL Asynchronous Replication

When a replica halts because the SQL thread encounters an error, you can resume replication by skipping the problematic event(s). Two common approaches are available. Methods to Skip Errors 1) Skip a...

Leave a Comment

Anonymous

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