Singleton
Guarantee a class has exactly one instance with a global access point — and why it's the most overused pattern of all.
Big idea
A Singleton guarantees one instance of a class and gives one global way to reach it. It's legitimate for things that are truly one-per-process — a configuration snapshot, a hardware device handle, a connection pool. But it's global state in a nice coat, so treat it as a last resort and prefer passing the one instance in.
The problem it solves
Two separate concerns get bundled:
- Exactly one instance. If two parts of the program each open their own pool of 50 database connections, you've doubled the load on the database. If two parts each load the config file, they may see different versions after an edit.
- Easy access. Passing the pool through twelve layers of constructors is tedious.
Think of it like
A country's official timekeeper. There is one reference clock, and everyone agrees to read time from it — two "official" clocks that disagree would be worse than useless. But if every appliance in your house were hard-wired to that one clock, you could never test your oven with a pretend time.
Structure
In Java or C#: a private constructor, a private static field, a public static getInstance(). In Python, the idiomatic singleton is simply a module-level object — modules are imported once.
In code
class Settings:
_instance = None
loads = 0
def __new__(cls): # classic: control creation itself
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._values = {"currency": "EUR"}
Settings.loads += 1
return cls._instance
def get(self, key):
return self._values[key]
a, b = Settings(), Settings()
print("same object:", a is b, "| loaded", Settings.loads, "time(s)")
# Pythonic: a module-level instance. Importing the module twice still gives one.
# settings.py: settings = Settings()
# elsewhere: from settings import settings
# Preferred where you can: create one instance at startup and pass it in.
class Invoice:
def __init__(self, settings): # dependency injection, not a global lookup
self.settings = settings
def total(self, amount):
return f"{amount:.2f} {self.settings.get('currency')}"
class FakeSettings: # trivial to substitute in a test
def get(self, key):
return "TEST"
print(Invoice(Settings()).total(9.5))
print(Invoice(FakeSettings()).total(9.5))output
same object: True | loaded 1 time(s)
9.50 EUR
9.50 TESTRun and edit it in the app.
The first part shows the classic mechanism. The last part shows why injection is usually better: Invoice still uses the one real Settings in production, but a test hands it a fake without monkey-patching a global.
Watch it
How to implement it (if you must)
- Make the constructor private (or intercept creation, as
__new__does above). - Store the instance in a static/class field; create it lazily on first access.
- In multithreaded code, guard lazy creation with a lock (or create eagerly at startup).
- Even then, have most code receive the instance as a parameter, and call
get_instance()only at the composition root.
What is the main downside of classes calling Settings.get_instance() directly throughout the code?
When to use it — and when not to
Reasonable: truly unique resources (a hardware device, a process-wide connection pool), read-only configuration loaded once, a logger facade. Avoid it for anything with mutable business state, anything you'll want to fake in tests, or as a shortcut to avoid passing parameters — that's how you end up with a "god" object everyone depends on.
Pros and cons
| Pros | Cons |
|---|---|
| Exactly one instance, created once | Global state: hidden coupling everywhere |
| Lazy initialisation | Tests leak state into each other |
| Simple access | Thread-safety pitfalls in lazy creation |
| Violates Single Responsibility: manages its own lifecycle and does its job |
How it relates
- Abstract Factory concrete factories and Facade objects are often single instances.
- Flyweight also shares instances, but many of them, keyed by their intrinsic state.
- Dependency Inversion is the usual alternative: create one instance in
main, inject it everywhere. - In the Logging framework case study, the logger registry is a deliberate, well-contained singleton.
What's the most idiomatic way to have one shared instance in Python?
Key terms
- Singleton — A class that ensures only one instance exists and provides global access to it.
- Lazy initialisation — Creating the instance on first use rather than at startup.
- Global state — Data reachable from anywhere, which creates hidden dependencies between distant code.
- Composition root — The place where the single instance should be created and handed to its users.