CreationalPythonverifiedVerified
Singleton Pattern in Python
Ensures a class has only one instance and provides a global point of access to it.
How to Implement the Singleton Pattern in Python
1Step 1: Define the Singleton using a metaclass
class SingletonMeta(type):
_instances: dict[type, "SingletonMeta"] = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class Singleton(metaclass=SingletonMeta):
def do_something(self) -> None:
print("Singleton method called")2Step 2: Verify only one instance exists
a = Singleton()
b = Singleton()
print(a is b) # True
a.do_something()"""Thread-safe Singleton with async initialization for database pool."""
import asyncio
import logging
import threading
from dataclasses import dataclass, field
from typing import Any
logger = logging.getLogger(__name__)
# [step] Define configuration and the thread-safe singleton
@dataclass(frozen=True)
class DatabaseConfig:
host: str = "localhost"
port: int = 5432
database: str = "app"
max_connections: int = 10
class DatabasePool:
_instance: "DatabasePool | None" = None
_lock = threading.Lock()
_init_task: asyncio.Task["DatabasePool"] | None = None
def __init__(self, config: DatabaseConfig) -> None:
self._config = config
self._connections: list[dict[str, Any]] = []
self._is_shutdown = False
@classmethod
async def get_instance(
cls, config: DatabaseConfig | None = None
) -> "DatabasePool":
"""Async-safe singleton accessor; concurrent callers share one init."""
if cls._instance is not None:
return cls._instance
with cls._lock:
if cls._instance is not None:
return cls._instance
if cls._init_task is None:
cfg = config or DatabaseConfig()
async def _init() -> "DatabasePool":
pool = cls(cfg)
await pool._initialize()
cls._instance = pool
return pool
cls._init_task = asyncio.ensure_future(_init())
return await cls._init_task
async def _initialize(self) -> None:
logger.info("Initializing pool with %d connections", self._config.max_connections)
for i in range(self._config.max_connections):
self._connections.append({"id": i, "active": False})
async def query(
self, sql: str, params: list[Any] | None = None
) -> list[dict[str, Any]]:
if self._is_shutdown:
raise RuntimeError("Pool has been shut down")
return [{"sql": sql, "params": params, "result": "mock"}]
async def shutdown(self) -> None:
logger.info("Shutting down database pool")
self._is_shutdown = True
self._connections.clear()
DatabasePool._instance = None
DatabasePool._init_task = None
@classmethod
def reset_instance(cls) -> None:
"""Reset for testing purposes."""
cls._instance = None
cls._init_task = NoneSingleton Pattern Architecture
hourglass_empty
Rendering diagram...
lightbulb
Singleton Pattern in the Real World
“Think of a country’s president. There can only be one at any time. When anyone needs to communicate with the president, they don’t create a new one—they access the existing one through the official channel (the static method).”