Skip to content
FollowtheDamnCode

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:

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

A private constructor and one shared instance

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

Three ways to get one instance — and the one to prefer
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 TEST

Run 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)

  1. Make the constructor private (or intercept creation, as __new__ does above).
  2. Store the instance in a static/class field; create it lazily on first access.
  3. In multithreaded code, guard lazy creation with a lock (or create eagerly at startup).
  4. 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

ProsCons
Exactly one instance, created onceGlobal state: hidden coupling everywhere
Lazy initialisationTests leak state into each other
Simple accessThread-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.