Contents
Chapter 24

Singleton

A singleton is the simplest design pattern: a class with exactly one instance. Before using a classic implementation, ask whether the language already solves the problem, the question When a Pattern Dissolves poses for every pattern. For the singleton, the language already has an answer.

A Module Is Already a Singleton

Python imports each module once and caches it in sys.modules, as Modules and Packages showed. Every import after the first produces the same module object. A module is a singleton, and everyone shares anything defined at module level, with one copy for the whole interpreter. One interpreter, not one machine. A process pool or an InterpreterPoolExecutor gives each worker its own sys.modules, so each builds its own copy and a write in one is invisible to the rest. A singleton is single within the interpreter that holds it, and every form in this chapter, the module included, has that limit.

Put the state in a module:

# config.py
print("config body runs")
settings: dict[str, str] = {}
# module_singleton.py
import config
import config as again

print(config is again, config.settings is again.settings)
#: config body runs
#: True True

Two import statements, one printed line. The first one runs config.py top to bottom and files the resulting module object in sys.modules under the name config. The second finds it there and skips the work, so the body runs once and builds one settings dict. That is the singleton: not a rule the class enforces, but a lookup the import system performs.

Every import of settings, from anywhere, hands back that same dict. Mutating it through one import is visible through every other:

# shared_config.py
from config import settings

settings["theme"] = "dark"
print(settings)
#: config body runs
#: {'theme': 'dark'}

No class, no ceremony. For most singleton needs, a module solves the problem.

Mutation makes the sharing work. Rebinding is the mistake that quietly ends it. from config import settings gives your module its own name for the same dict object named by config.settings. Mutating through your name, settings["theme"] = "dark", changes that shared object, so every module sees it. But settings = {} in your module rebinds only your module’s name, and the two modules silently diverge: config.settings still holds the old dict, while your code now talks to a private one. To replace the whole value, go through the module: import config, then config.settings = {...}. Mutate through any name. Rebind only through the module.

Sharing also depends on the name. sys.modules uses the module name as its key, and the file you launch runs under the name __main__. If that file is config.py, a later import config finds no cached entry, runs the body again, and builds a second module object with its own settings. Keep singleton state in a module you import, not in the script you run.

When You Want a Class, Cache the Instance

The goal is that every construction returns the same object. The simplest approach hides construction behind a cached factory: functools.cache applied to a constructor function, an ordinary function that builds and returns an instance of a class. The constructor function stands in for a direct call to the class constructor.

functools.cache (Caching) memoizes a function. The first call with a given set of arguments runs the function and stores the result. Every repeat call with those arguments returns the stored result. A constructor function with no arguments has only one possible call, so caching it constructs the instance once and returns that same object forever:

# singleton_cached_factory.py
from dataclasses import dataclass, field
from functools import cache

@dataclass
class Settings:
    data: dict[str, str] = field(default_factory=dict)

@cache
def settings() -> Settings:
    return Settings()

a = settings()
b = settings()
assert a is b
a.data["theme"] = "dark"
print(b)
#: Settings(data={'theme': 'dark'})

Giving the constructor function a parameter breaks the guarantee. functools.cache keys its cache on the arguments, so each distinct argument value gets its own entry and its own instance:

# singleton_cached_factory_footgun.py
from dataclasses import dataclass, field
from functools import cache

@dataclass
class Settings:
    data: dict[str, str] = field(default_factory=dict)

@cache
def settings(env: str = "prod") -> Settings:
    return Settings()

print(settings("prod") is settings("dev"))
#: False

One parameter turns “one singleton” into one singleton per argument value. Keep the constructor function’s signature empty, or accept that you built a cache, not a singleton.

Nothing Keeps the Class Private

Nothing stops a caller from writing Settings() and getting a second instance. Naming the class _Settings marks it internal and keeps it out of from module import *, and that marking is as far as Python goes. A second underscore adds no strength. The compiler mangles names only inside a class body, so at module level __Settings is the plain name it looks like. Inside a class body the compiler rewrites m.__Settings into a lookup for _TheClass__Settings, and that lookup fails.

This listing keeps the bare name for a reason that outlasts the convention. settings() returns a Settings, so the class already appears in the module’s public signature. A caller who annotates the result must write that name, and a type outsiders must name is not private, whatever it starts with. _Settings fits a type that never leaves the module.

Two stronger-looking moves fail the same way. Deleting the name after building the instance leaves the class reachable: type(settings()) hands it back. Defining the class inside settings() leaves the module no name for it, since @cache runs that body once and the class lives in the function’s locals. type(settings()) still recovers the class.

Nesting costs the return annotation as well. def settings() -> Settings still parses and runs, because an annotation evaluates only when something reads it, so a clean run proves nothing about the name. Whatever reads the annotation searches the scope containing the function, while the class lives in the function’s own locals. A type checker reports an unresolved reference, and inspect.get_annotations() raises a NameError. The signature must name something reachable, so nesting costs you either the annotation or a separate Protocol to name in its place.

Privacy in Python is advice, not enforcement. An underscore asks callers to stay out, and nothing makes them. Rethinking Objects makes the same case about hidden data, where a getter hands back a reference to the internals it should protect. The reachable class is also useful when a test needs a fresh, uncached Settings.

Tests, Threads, and Locks

Three implementation notes:

  1. A singleton holds shared state, and shared state leaks between tests. The cached factory has an escape hatch the classic forms lack: settings.cache_clear() discards the instance, so each test can start fresh.

  2. Every lazy singleton has a first-call race under threads. Concurrent first calls can each run the constructor, and each caller can end up holding a different object, with only one of them staying in the cache. With a constructor slow enough to widen that race, eight threads calling settings() at once ran the constructor eight times and handed back eight different objects. When threads can arrive before the singleton exists, create it eagerly instead: call settings() once at import time, or use the module form, which the import system builds exactly once.

  3. A lock is the other fix for that race, but not in the obvious place. A threading.Lock around the cached function’s body changes nothing, because every thread has already missed the cache before reaching the lock. They serialize, each still builds an object, and the cache keeps whichever finished last. The check must run inside the lock, as singleton_locked_settings.py shows.

The race is easy to see with a wide enough window:

# singleton_cached_race.py
import time
from concurrent.futures import ThreadPoolExecutor
from dataclasses import dataclass, field
from functools import cache

@dataclass
class Settings:
    data: dict[str, str] = field(default_factory=dict)

    def __post_init__(self) -> None:
        time.sleep(0.05)  # Widen the first-call window

@cache
def settings() -> Settings:
    return Settings()

with ThreadPoolExecutor(max_workers=8) as pool:
    built = list(pool.map(lambda _: settings(), range(8)))
print(len({id(s) for s in built}) > 1)
#: True

Eight threads, more than one object. Every thread checks the cache before any of them has filled it, so each runs the constructor and hands its caller a different object. Only the last one to finish stays in the cache. The other seven are already in the hands of their callers.

@cache disappears below, because it no longer makes the object single:

# singleton_locked_settings.py
import threading
import time
from concurrent.futures import ThreadPoolExecutor
from dataclasses import dataclass, field
from typing import Final

@dataclass
class Settings:
    data: dict[str, str] = field(default_factory=dict)

    def __post_init__(self) -> None:
        time.sleep(0.05)  # Widen the first-call window

_lock: Final[threading.Lock] = threading.Lock()
_instance: Settings | None = None

def settings() -> Settings:
    global _instance
    with _lock:
        if _instance is None:
            _instance = Settings()
    return _instance

with ThreadPoolExecutor(max_workers=8) as pool:
    built = list(pool.map(lambda _: settings(), range(8)))
print(len({id(s) for s in built}))
#: 1

settings() declares global for _instance and leaves _lock undeclared. The mutate-versus-rebind distinction from A Module Is Already a Singleton reappears here, from inside a function. global governs rebinding, not use. with _lock: reads the name, even though acquiring and releasing changes that lock’s state, from unlocked to locked and back. Changing an object is not rebinding a name. _instance differs because the function assigns to it. Python decides at compile time that a name a function assigns anywhere is local everywhere in that function, so without the global declaration, if _instance is None reads an unassigned local and raises an UnboundLocalError. Mutate through any name. Declare only what you rebind.

One thread finds _instance empty and builds it. The rest wait on the lock, and each finds _instance already filled. Under the same eight-thread race, the cached version produced eight objects. This version produces one, as the printed count confirms. The sleep stands in for a constructor that does real work, such as opening a file or a connection. Without the sleep, the cached version showed no duplicates across twenty trials, and that silence is the more dangerous case. A window too narrow to reproduce is still a window.

Every call now acquires the lock, including the thousands that arrive long after the object exists. That is the price of laziness under threads.

The classic escape is double-checked locking: test _instance before taking the lock, take it when the test finds the object missing, then test again inside. The second test is the one note 3 requires. The first exists to skip the lock once the object is there. Double-checked locking works, but both checks must be exactly right, and a subtle mistake reintroduces the race the lock exists to close. That is a bad trade for saving one lock acquisition. Eager creation is a better answer when you can build the object at import time:

# singleton_eager_factory.py
from dataclasses import dataclass, field
from functools import cache

@dataclass
class Settings:
    data: dict[str, str] = field(default_factory=dict)

@cache
def settings() -> Settings:
    return Settings()

settings()  # Build it before any thread can race for it
print(settings() is settings())
#: True

The priming call is safe for the same reason the module form is: the import system runs a module body once, so the object exists before any thread can ask for it. The race needs laziness, and this listing gives it up on purpose.

If you need the class to hand back one instance from its own constructor, override __new__(), as singleton_class_variable.py does.

Modules and cached factories, primed at import time if threads are in play, should cover your singleton needs. The rest of this chapter is here for the techniques it demonstrates, not because you need these forms.

The Classic Implementations

To address languages like C++ and Java, GoF Design Patterns builds the singleton with more apparatus. Each variation shown here does more work than the module or the cached factory above.

The first controls creation by delegating to a single instance of a private nested class. The rest reach it by other means: a class variable and a decorator, while Borg trades one object for one shared set of state.

Lazy Creation

The classic approach is lazy: it builds the inner object on the first call, and that is why it needs the None sentinel and the if guard.

# singleton_pattern.py
from dataclasses import dataclass, field
from typing import Any, ClassVar

class OnlyOne:
    @dataclass
    class __OnlyOne:
        val: list[str] = field(default_factory=list)

    instance: ClassVar[__OnlyOne | None] = None

    def __init__(self, arg: str) -> None:
        if OnlyOne.instance is None:
            OnlyOne.instance = OnlyOne.__OnlyOne()
        OnlyOne.instance.val.append(arg)

    def __getattr__(self, name: str) -> Any:
        return getattr(self.instance, name)

x = OnlyOne("sausage")
print(x.val)
#: ['sausage']
y = OnlyOne("eggs")
print(y.val)
#: ['sausage', 'eggs']
z = OnlyOne("spam")
print(z.val)
#: ['sausage', 'eggs', 'spam']
# Distinct wrappers (x is not y), one shared inner instance:
print(x is y, x.instance is y.instance is z.instance)
#: False True

Because the inner class’s name starts with a double underscore, the compiler mangles it to _OnlyOne__OnlyOne wherever it appears inside OnlyOne’s body. OnlyOne.__OnlyOne, written from outside the class, asks for an attribute that does not exist under that name, so it fails at runtime with AttributeError, not at type-checking time. The outer class controls creation through its constructor. The first construction of an OnlyOne initializes instance. Every later one reuses that inner object, and each construction appends its argument to that object’s shared list. __getattr__() delegates access. Python calls it only when ordinary attribute lookup fails, so a name the wrapper does not have, such as val, falls through to the inner object. The distinct OnlyOne instances all proxy to the same __OnlyOne object.

__getattr__() returns Any, and that Any stays. instance is one declared field and can say __OnlyOne | None, while __getattr__() answers for every name Python fails to find on the wrapper, so its return type is whatever the inner object holds under that name, an open set no annotation can list. Delegation gives up static knowledge to forward every name, the cost Surrogate pays throughout.

The laziness is a choice. When the inner object needs nothing from that first call, you can create it eagerly in the class body instead, instance: ClassVar[__OnlyOne] = __OnlyOne(). That removes the sentinel, the guard, and the first-call race the cached factory met under threads, at the cost of building the object whether or not anything uses it. (The bare __OnlyOne() works because the nested class exists at that point in the body. The qualified OnlyOne.__OnlyOne() fails, since the name OnlyOne stays unbound until its own class body finishes running.) Exercise 1 makes that change.

Either way, OnlyOne is a lot of code for what a module does on its own.

One Instance in a Class Variable

The nested private class is optional. Here you keep the single instance in a class variable. __new__(), the method that creates an instance, builds it when needed and returns it as the result of every construction:

# singleton_class_variable.py
from typing import ClassVar

class SingletonClassVar:
    val: list[str]
    __instance: ClassVar[SingletonClassVar | None] = None

    def __new__(cls, arg: str) -> SingletonClassVar:
        if SingletonClassVar.__instance is None:
            SingletonClassVar.__instance = (
                object.__new__(cls))
            SingletonClassVar.__instance.val = []
        SingletonClassVar.__instance.val.append(arg)
        return SingletonClassVar.__instance

x = SingletonClassVar("sausage")
y = SingletonClassVar("eggs")
z = SingletonClassVar("spam")
print(x.val, x is y is z, isinstance(x, SingletonClassVar))
#: ['sausage', 'eggs', 'spam'] True True

object.__new__(cls) builds a SingletonClassVar, so every construction hands back that same instance and isinstance() reports True. Python honors whatever object __new__() returns, and that return value decides whether __init__() runs. When __new__() returns an instance of the class under construction, Python runs __init__() on it, so a singleton __new__() triggers __init__() on the shared instance after every construction. SingletonClassVar defines no __init__(), so __new__() does all the work. A __new__() that returned some other object would skip __init__() and fail isinstance() as well.

Borg: Singleton By Inheritance

Alex Martelli observes that what you usually want is not one object but one shared set of state. People can create as many objects as they like, as long as they all share the same data. He called that design the Borg.1 A Borg points every instance’s __dict__ at the same storage:

x, y, and z are three distinct objects, but every dict points at the same _shared_state, so the last write wins for all three

The previous singleton designs stood alone; you reuse Borg through inheritance:

# singleton_borg.py
from typing import Any, ClassVar

class Borg:
    _shared_state: ClassVar[dict[str, Any]] = {}

    def __init__(self) -> None:
        self.__dict__ = self._shared_state

class Singleton(Borg):
    def __init__(self, arg: str) -> None:
        super().__init__()
        self.val = arg

    def __str__(self) -> str:
        return self.val

x = Singleton("sausage")
y = Singleton("eggs")
z = Singleton("spam")
# Last write wins: distinct objects, one shared __dict__:
print(x.val, x is y, x.__dict__ is y.__dict__ is z.__dict__)
#: spam False True

The nested class above was a @dataclass; Singleton cannot be one. The sharing depends on super().__init__ rebinding self.__dict__ to _shared_state, and a dataclass generates its own __init__ that assigns the fields and never calls the base __init__, so each instance keeps its own __dict__. The code still runs; the class has quietly stopped being a Borg. A __post_init__ that does the rebinding fails differently: it runs after __init__ has assigned the fields, so the rebinding discards them. The hand-written __init__ makes the sharing work, and silently losing the sharing is worse than failing outright.

The sharing also reaches further than it looks. _shared_state is one dict on Borg, so every subclass shares it, not merely every instance of a single subclass. A second subclass alongside Singleton writes into the same dict, so constructing one of each leaves both objects reading the value set last. A subclass that needs storage of its own declares it: class Singleton(Borg): _shared_state: ClassVar[dict[str, Any]] = {}.

The test confirms the objects differ but share one set of state. Borg has no cache_clear(): whatever one test leaves in _shared_state is still there for the next. A pytest fixture closes that gap by clearing the dict before each test:

# test_singleton_borg.py
import pytest
from singleton_borg import Borg, Singleton

@pytest.fixture(autouse=True)
def reset_shared_state() -> None:
    Borg._shared_state.clear()

def test_borg_shares_state_but_not_identity() -> None:
    x = Singleton("first")
    y = Singleton("second")
    assert x is not y  # Distinct objects
    assert x.val == y.val  # But sharing one set of state
    assert x.val == "second"

def test_pollutes_shared_state() -> None:
    setattr(Singleton("first"), "extra", "leftover")

def test_fixture_cleared_it() -> None:
    y = Singleton("second")
    assert not hasattr(y, "extra")  # Reset ran

Singleton by Class Decorator

A class decorator can wrap a class so that calling it returns a cached instance:

# singleton_class.py
from typing import Any

class singleton:
    def __init__(self, constructor: type) -> None:
        self.constructor = constructor
        self.instance: Any = None

    def __call__(self, *args: Any, **kwargs: Any) -> Any:
        print(f"singleton.__call__({args}, {kwargs})")
        if self.instance is None:
            print(
                f"constructing {self.constructor.__name__}")
            self.instance = self.constructor(
                *args, **kwargs)
        else:
            print(
                f"using cached {self.constructor.__name__}")
            print(f"discarding {args}, {kwargs}")
        return self.instance

@singleton
class Registry:
    def __init__(self, name: str, *,
                 limit: int = 10) -> None:
        print(f"Registry.__init__({name}, {limit})")
        self.name = name
        self.limit = limit
        self.items: list[str] = []

first = Registry("primary", limit=3)
#: singleton.__call__(('primary',), {'limit': 3})
#: constructing Registry
#: Registry.__init__(primary, 3)
first.items.append("spam")
first.items.append("eggs")
second = Registry("secondary", limit=99)
#: singleton.__call__(('secondary',), {'limit': 99})
#: using cached Registry
#: discarding ('secondary',), {'limit': 99}
print(first is second, second.name,
      second.limit, second.items)
#: True primary 3 ['spam', 'eggs']

@singleton on Registry runs Registry = singleton(Registry). The name Registry now refers to the decorated instance rather than to the class. Why does __call__() intercept the constructor for a Registry? To evaluate obj(...), Python looks up __call__() on the type of obj (Surrogate examines this type-based lookup in full). For an ordinary class C, type(C) is type, and the parentheses run type.__call__(), the machinery that invokes __new__() and then __init__(). After decoration, type(Registry) is singleton, so the same parentheses run singleton.__call__() instead, and the wrapped class’s constructor runs only when that method decides to call it. __call__() forwards *args and **kwargs to the constructor of the wrapped class, so Registry("primary", limit=3) reaches the real constructor unchanged.

Only the first call constructs a Registry. Every later constructor call returns the cached instance and discards the constructor arguments, so Registry("secondary", limit=99) creates no new object. A caller who believes those arguments took effect holds an object configured by someone else.

isinstance(first, Registry) and class Sub(Registry) both raise:

# test_singleton_class.py
import pytest
from singleton_class import Registry

def test_isinstance_rejects_the_decorated_name() -> None:
    with pytest.raises(TypeError,
                       match="arg 2 must be a type"):
        isinstance(Registry("primary"), Registry)  # type: ignore

def test_subclassing_the_decorated_name_fails() -> None:
    with pytest.raises(TypeError,
                       match="takes 2 positional"):
        class Sub(Registry):  # type: ignore
            pass

ty and Pyright reject Sub statically: its base has type singleton, not a class. Under mypy, which does not apply a class decorator’s return type, Registry is still a class and Sub passes. At runtime the class statement raises a TypeError. singleton.__init__() takes two positional arguments and receives four, because a class statement hands the name, bases, and namespace to its metaclass, and Python takes that metaclass from the type of the base, which is singleton. Nothing in class Sub(Registry) mentions singleton, so the error names a class that does not appear in the failing line. That is the confusion a class decorator costs you. singleton_class_variable.py keeps the name pointing at a real class, and that is the reason to prefer it.

A metaclass can also intercept construction. Metaprogramming shows that singleton: its metaclass overrides __call__(), and that override skips __init__() on every later construction, so the first call’s arguments win. In a class that overrides __new__() instead, as singleton_class_variable.py does, __new__() still runs on every call, unlike the metaclass form above. That listing puts its work inside __new__() itself, so later calls append to the shared instance instead of overwriting it. Metaprogramming also covers __init_subclass__() and __set_name__(), the simpler hooks that replace most metaclasses. A singleton needs none of this machinery.

Which Should You Use?

Use the lightest tool that fits:

The elaborate GoF Design Patterns singleton is largely a workaround for languages where a module is not a first-class, single-instance namespace. In Python, most of the ceremony falls away.

Exercises

  1. singleton_pattern.py waits for the first construction to build its inner object. Modify it to use eager initialization, creating the inner instance in the class body, and remove the sentinel and the guard. What did the change cost, and which failure from Tests, Threads, and Locks can no longer occur?
  2. Using singleton_cached_factory.py as a starting point, create a factory that manages a fixed pool of objects (say, database connections) and hands them out, rather than a single instance.
  3. Rewrite one of the class-based singletons above as a module, and argue which you would use in real code.
  4. In shared_config.py, replace the mutation with a rebinding, settings = {"theme": "dark"}, and add import config plus print(config.settings) at the end. Predict both printed values before running it, and explain the difference using the binding-versus-mutation distinction from A Module Is Already a Singleton.
  5. Add a threading.Lock inside settings() in singleton_cached_race.py, wrapping only the body of the cached function, and run it. Explain why the object count does not drop to one, then fix it without a lock.
  6. Give singleton_borg.py a second Borg subclass and construct one of each. Explain the value you get back, and change the code so the two subclasses keep separate shared state.