Skip to content

Singleton Pattern

Category: Creational Pattern

Overview

Ensure a class has only one instance and provide a global point of access to it. This pattern restricts instantiation of a class to a single object, useful for managing shared resources like configuration settings, logging, or database connections.

Usage Guidelines

Use when:

  • Only one instance of a class should exist in the system
  • The instance needs to be accessible from anywhere in the application
  • Managing shared resources like database connections, thread pools, or caches
  • Centralized configuration that must be consistent across the application

Avoid when:

  • Singletons make unit testing harder due to global state
  • Creates hidden dependencies throughout the codebase
  • Complex synchronization needed for thread-safe access
  • You might need multiple instances in the future

Implementation

from threading import Lock
from typing import Any

class SingletonMeta(type):
    """Metaclass that creates a Singleton base class.

    This implementation is thread-safe and ensures that only one instance
    of the class exists across multiple threads.
    """

    _instances: dict[type, Any] = {}
    _lock: Lock = Lock()

    def __call__(cls, *args: Any, **kwargs: Any) -> Any:
        """Create or return the singleton instance.

        Args:
            *args: Variable length argument list.
            **kwargs: Arbitrary keyword arguments.

        Returns:
            The singleton instance of the class.
        """
        with cls._lock:
            if cls not in cls._instances:
                instance = super().__call__(*args, **kwargs)
                cls._instances[cls] = instance
        return cls._instances[cls]

class DatabaseConnection(metaclass=SingletonMeta):
    """Example singleton class representing a database connection.

    This class can only have one instance throughout the application lifecycle.
    """

    def __init__(self) -> None:
        """Initialize the database connection."""
        self.connection_string = "Connected to database"
        self.queries_executed = 0

    def execute_query(self, query: str) -> str:
        """Execute a database query.

        Args:
            query: The SQL query to execute.

        Returns:
            Result message indicating query execution.
        """
        self.queries_executed += 1
        return f"Executed: {query}"

Usage

# Metaclass-based singleton
db1 = DatabaseConnection()
db2 = DatabaseConnection()
assert db1 is db2  # True - same instance
print(db1.execute_query("SELECT * FROM users"))

Trade-offs

Benefits:

  1. Controlled access through single point of control
  2. Reduced namespace pollution while providing global access
  3. Lazy initialization with instance created only when first needed
  4. Thread safety with proper implementation ensuring safe concurrent access

Drawbacks:

  1. Introduces global state making code harder to reason about
  2. Hard to mock or replace in unit tests
  3. Classes depending on singletons have hidden dependencies
  4. Requires careful synchronization in multi-threaded environments

Real-World Examples

  • Logging systems used throughout application
  • Configuration managers centrally accessible
  • Database connection pools
  • Cache managers shared across application
  • Factory Method
  • Abstract Factory
  • Facade
  • State
  • Flyweight

API Reference

design_patterns.creational.singleton

Singleton Pattern Module

The Singleton pattern ensures that a class has only one instance and provides a global point of access to that instance. This is useful for managing shared resources like configuration settings, logging, or database connections.

This module demonstrates three implementations: 1. Metaclass-based Singleton (thread-safe) 2. Decorator-based Singleton 3. Module-level Singleton (Pythonic approach)

Example

Using the metaclass-based singleton:

class DatabaseConnection(metaclass=SingletonMeta):
    def __init__(self):
        self.connection = "Connected to database"

db1 = DatabaseConnection()
db2 = DatabaseConnection()
assert db1 is db2  # True, same instance

ConfigurationManager

Pythonic singleton using class attributes.

This implementation uses a class attribute to store the single instance, which is a common Python idiom for singletons.

Source code in src/design_patterns/creational/singleton.py
class ConfigurationManager:
    """Pythonic singleton using class attributes.

    This implementation uses a class attribute to store the single instance,
    which is a common Python idiom for singletons.
    """

    _instance: ConfigurationManager | None = None
    _lock: Lock = Lock()

    def __new__(cls) -> ConfigurationManager:
        """Create or return the singleton instance.

        Returns:
            The singleton instance.
        """
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = super().__new__(cls)
                    cls._instance._initialized = False
        return cls._instance

    def __init__(self) -> None:
        """Initialize the configuration manager."""
        if self._initialized:
            return
        self.config: dict[str, Any] = {}
        self._initialized = True

    def set(self, key: str, value: Any) -> None:
        """Set a configuration value.

        Args:
            key: The configuration key.
            value: The configuration value.
        """
        self.config[key] = value

    def get(self, key: str, default: Any = None) -> Any:
        """Get a configuration value.

        Args:
            key: The configuration key.
            default: Default value if key not found.

        Returns:
            The configuration value or default.
        """
        return self.config.get(key, default)

__init__()

Initialize the configuration manager.

Source code in src/design_patterns/creational/singleton.py
def __init__(self) -> None:
    """Initialize the configuration manager."""
    if self._initialized:
        return
    self.config: dict[str, Any] = {}
    self._initialized = True

__new__()

Create or return the singleton instance.

Returns:

Type Description
ConfigurationManager

The singleton instance.

Source code in src/design_patterns/creational/singleton.py
def __new__(cls) -> ConfigurationManager:
    """Create or return the singleton instance.

    Returns:
        The singleton instance.
    """
    if cls._instance is None:
        with cls._lock:
            if cls._instance is None:
                cls._instance = super().__new__(cls)
                cls._instance._initialized = False
    return cls._instance

get(key, default=None)

Get a configuration value.

Parameters:

Name Type Description Default
key str

The configuration key.

required
default Any

Default value if key not found.

None

Returns:

Type Description
Any

The configuration value or default.

Source code in src/design_patterns/creational/singleton.py
def get(self, key: str, default: Any = None) -> Any:
    """Get a configuration value.

    Args:
        key: The configuration key.
        default: Default value if key not found.

    Returns:
        The configuration value or default.
    """
    return self.config.get(key, default)

set(key, value)

Set a configuration value.

Parameters:

Name Type Description Default
key str

The configuration key.

required
value Any

The configuration value.

required
Source code in src/design_patterns/creational/singleton.py
def set(self, key: str, value: Any) -> None:
    """Set a configuration value.

    Args:
        key: The configuration key.
        value: The configuration value.
    """
    self.config[key] = value

DatabaseConnection

Example singleton class representing a database connection.

This class can only have one instance throughout the application lifecycle.

Source code in src/design_patterns/creational/singleton.py
class DatabaseConnection(metaclass=SingletonMeta):
    """Example singleton class representing a database connection.

    This class can only have one instance throughout the application lifecycle.
    """

    def __init__(self) -> None:
        """Initialize the database connection."""
        self.connection_string = "Connected to database"
        self.queries_executed = 0

    def execute_query(self, query: str) -> str:
        """Execute a database query.

        Args:
            query: The SQL query to execute.

        Returns:
            Result message indicating query execution.
        """
        self.queries_executed += 1
        return f"Executed: {query}"

__init__()

Initialize the database connection.

Source code in src/design_patterns/creational/singleton.py
def __init__(self) -> None:
    """Initialize the database connection."""
    self.connection_string = "Connected to database"
    self.queries_executed = 0

execute_query(query)

Execute a database query.

Parameters:

Name Type Description Default
query str

The SQL query to execute.

required

Returns:

Type Description
str

Result message indicating query execution.

Source code in src/design_patterns/creational/singleton.py
def execute_query(self, query: str) -> str:
    """Execute a database query.

    Args:
        query: The SQL query to execute.

    Returns:
        Result message indicating query execution.
    """
    self.queries_executed += 1
    return f"Executed: {query}"

Logger

Example singleton logger class using decorator pattern.

This logger ensures all parts of the application use the same logging instance.

Source code in src/design_patterns/creational/singleton.py
@singleton_decorator
class Logger:
    """Example singleton logger class using decorator pattern.

    This logger ensures all parts of the application use the same logging instance.
    """

    def __init__(self) -> None:
        """Initialize the logger."""
        self.logs: list[str] = []

    def log(self, message: str) -> None:
        """Log a message.

        Args:
            message: The message to log.
        """
        self.logs.append(message)

    def get_logs(self) -> list[str]:
        """Get all logged messages.

        Returns:
            List of all logged messages.
        """
        return self.logs

__init__()

Initialize the logger.

Source code in src/design_patterns/creational/singleton.py
def __init__(self) -> None:
    """Initialize the logger."""
    self.logs: list[str] = []

get_logs()

Get all logged messages.

Returns:

Type Description
list[str]

List of all logged messages.

Source code in src/design_patterns/creational/singleton.py
def get_logs(self) -> list[str]:
    """Get all logged messages.

    Returns:
        List of all logged messages.
    """
    return self.logs

log(message)

Log a message.

Parameters:

Name Type Description Default
message str

The message to log.

required
Source code in src/design_patterns/creational/singleton.py
def log(self, message: str) -> None:
    """Log a message.

    Args:
        message: The message to log.
    """
    self.logs.append(message)

SingletonMeta

Bases: type

Metaclass that creates a Singleton base class.

This implementation is thread-safe and ensures that only one instance of the class exists across multiple threads.

Source code in src/design_patterns/creational/singleton.py
class SingletonMeta(type):
    """Metaclass that creates a Singleton base class.

    This implementation is thread-safe and ensures that only one instance
    of the class exists across multiple threads.
    """

    _instances: dict[type, Any] = {}
    _lock: Lock = Lock()

    def __call__(cls, *args: Any, **kwargs: Any) -> Any:
        """Create or return the singleton instance.

        Args:
            *args: Variable length argument list.
            **kwargs: Arbitrary keyword arguments.

        Returns:
            The singleton instance of the class.
        """
        with cls._lock:
            if cls not in cls._instances:
                instance = super().__call__(*args, **kwargs)
                cls._instances[cls] = instance
        return cls._instances[cls]

__call__(*args, **kwargs)

Create or return the singleton instance.

Parameters:

Name Type Description Default
*args Any

Variable length argument list.

()
**kwargs Any

Arbitrary keyword arguments.

{}

Returns:

Type Description
Any

The singleton instance of the class.

Source code in src/design_patterns/creational/singleton.py
def __call__(cls, *args: Any, **kwargs: Any) -> Any:
    """Create or return the singleton instance.

    Args:
        *args: Variable length argument list.
        **kwargs: Arbitrary keyword arguments.

    Returns:
        The singleton instance of the class.
    """
    with cls._lock:
        if cls not in cls._instances:
            instance = super().__call__(*args, **kwargs)
            cls._instances[cls] = instance
    return cls._instances[cls]

singleton_decorator(cls)

Decorator that converts a class into a singleton.

Parameters:

Name Type Description Default
cls type

The class to convert into a singleton.

required

Returns:

Type Description
type

A wrapper class that implements singleton behavior.

Source code in src/design_patterns/creational/singleton.py
def singleton_decorator(cls: type) -> type:
    """Decorator that converts a class into a singleton.

    Args:
        cls: The class to convert into a singleton.

    Returns:
        A wrapper class that implements singleton behavior.
    """
    instances: dict[type, Any] = {}
    lock = Lock()

    def get_instance(*args: Any, **kwargs: Any) -> Any:
        """Get or create the singleton instance.

        Args:
            *args: Variable length argument list.
            **kwargs: Arbitrary keyword arguments.

        Returns:
            The singleton instance.
        """
        with lock:
            if cls not in instances:
                instances[cls] = cls(*args, **kwargs)
        return instances[cls]

    return get_instance  # type: ignore[return-value]