1. The Problem
Problem: A Resource That Must Exist Exactly Once
Some objects represent global system resources where multiple instances are incorrect or dangerous.
Examples:
- Application configuration
- Logger
- Database connection pool
- Cache
- Feature-flag registry
If multiple instances are created:
- State diverges
- Resources are wasted
- Behavior becomes non-deterministic
Naive code makes this easy to break:
config1 = Config()
config2 = Config() # Oops — different instance
We need exactly one instance, shared across the system, with controlled creation.
2. The Singleton Pattern: Controlled Global State
The Singleton Pattern solves this by:
- Restricting instantiation to a single object
- Providing a global access point
- Centralizing ownership of shared state
Core idea:
There is exactly one instance, and everyone talks to the same one.
A Singleton has three properties:
- Private construction – cannot be freely instantiated
- Single stored instance – created once
- Global access method – returns that instance
Use Singleton when:
- The concept is inherently singular
- State must be globally consistent
- Initialization should happen once
- Multiple instances would be a bug, not a feature
3. Implementation: Singleton in Python
Example: Application Configuration
Goal: One configuration object shared everywhere.
Basic Singleton (Lazy Initialization)
class AppConfig:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._load()
return cls._instance
def _load(self):
self.debug = True
self.db_url = "postgres://localhost"
Usage:
config1 = AppConfig()
config2 = AppConfig()
assert config1 is config2 # Same instance
Thread-Safe Singleton
For multi-threaded environments:
import threading
class AppConfig:
_instance = None
_lock = threading.Lock()
def __new__(cls):
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._load()
return cls._instance
def _load(self):
self.debug = True
This guarantees:
- Exactly one instance
- Safe concurrent initialization
4. When to Use Singleton (and When Not To)
Use Singleton when:
- The object models a single system-wide concept
- Global consistency matters
- Creation is expensive or stateful
- You want controlled lifecycle
Avoid Singleton when:
- You just want shared behavior (use stateless functions)
- You need multiple independent instances later
- Testability matters more than global access
- Dependency injection can solve the problem cleanly
Common pitfalls:
- Hiding dependencies (implicit globals)
- Making everything a Singleton
- Treating Singleton as a shortcut for poor architecture
- Using Singleton where immutability or DI is better
5. Singleton vs Alternatives
| Need | Better Option |
|---|---|
| Shared logic | Static functions |
| Shared immutable data | Constants / config objects |
| Shared mutable state | Singleton |
| Test-friendly design | Dependency Injection |
Singleton is not about convenience. It is about correctness under constraint.
Use it only when the domain demands one — and only one — instance.