Why does a `__new__`-based singleton re-run `__init__` on every call, and how do you stop it?
answer
- Controlling which object is not controlling both steps
- The cached object still passes the type check
- Nothing tracks whether setup already ran
- Setup that must happen once must move earlier
- Guard flag, or setup in __new__
basics
~20 sBecause type.call runs init on whatever new returns whenever that object is an instance of the class — cached or freshly made. Move one-time setup into new, make init idempotent, or build the shared object in a factory instead.
solid answer
~50 sCaching the instance in `__new__` only controls *which* object comes back; it does not suppress the second step. `type.__call__` sees an instance of the class and calls `__init__` on it again with the new call's arguments, so every later `Client()` overwrites the state the first one configured. In an ETL export client this shows up as an intermittent timeout: one module configures the shared client with a 45-second timeout for a cold warehouse start, an unrelated module later does a bare `Client()`, `__init__` resets the timeout to its 5-second default, and the next cold export dies. The fixes, in order of preference: create the object in a factory function or a module-level instance instead of hijacking `__new__`; or perform setup inside `__new__` where it happens once; or guard `__init__` with an already-initialized flag. Whichever you choose, take a `threading.Lock` around the check-and-create.
code
python · 15 linesclass ExportClient:
_instance = None
def __new__(cls, timeout=5.0):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self, timeout=5.0):
self.timeout = timeout
tuned = ExportClient(timeout=45.0)
ExportClient()
print(tuned.timeout)go deeper
Recall the mechanism rather than the cure: calling a class always tries to initialize the object it gets back, so returning a cached one does not mean the setup code is skipped.
Explain why the isinstance guard in the call protocol passes for a cached instance, and show at least one working fix — setup moved into __new__, or an already-initialized guard in the initializer.
Diagnose it as a state bug, not a syntax bug: a shared object whose configuration is silently reset produces intermittent, environment-dependent failures. Cover the lock around check-and-create and the subclass attribute-lookup trap.
Own the position on process-global mutable state — what it costs in testability and in traceability of a misconfiguration — and set the house rule for when a shared object is passed explicitly rather than reached for through a class call.
The singleton written with `__new__` is one of the most common patterns in Python code and one of the most commonly broken, because it half-controls a two-step protocol. Calling a class runs `type.__call__`, which calls `__new__` for an object and then, **if that object is an instance of the class**, calls `__init__` on it with the same arguments. A caching `__new__` returns the stored instance — which is, of course, an instance of the class — so the guard passes and the initializer runs again. The object's identity is shared; its state is re-set on every call. **What that looks like in production.** Consider a client that runs an ETL export into a warehouse. Start-up code creates it with a long timeout because the warehouse takes about 45 seconds to come out of a cold start; a request-handling module elsewhere later writes a bare `ExportClient()` to "get the shared client". That second call re-enters `__init__`, which reassigns the default 5-second timeout onto the very same object the first module tuned. The failure is an intermittent export timeout: it depends entirely on whether the bare call happened to run before the next cold-start export, so it survives every test, reproduces on no developer machine, and looks like a network problem. Nothing raises, nothing logs, and the object is correct by identity while wrong by state — which is exactly why this is a senior question. The diagnostic instinct worth demonstrating is to stop trusting the singleton's state and print or log the configuration at use time rather than at construction time; the value flipping between calls names the cause immediately. ```python class ExportClient: _instance = None def __new__(cls, timeout=5.0): if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance def __init__(self, timeout=5.0): self.timeout = timeout # runs on every call tuned = ExportClient(timeout=45.0) ExportClient() print(tuned.timeout) # 5.0 - the tuned value is gone ``` **Fix one, and the best one: do not use `__new__`.** A module-level instance created once at import, or a factory function that builds and returns a cached object, expresses "one shared object" without overloading construction. Calling the class then always means "make me one", which is what every reader expects it to mean, and the shared object can be replaced in tests by passing a different one in. Hiding sharing inside `__new__` makes `ExportClient()` lie about what it does. **Fix two: put the setup in `__new__`.** If the class must remain the entry point, do the configuration where the object is chosen, and define no `__init__` at all. Then there is no second step to re-run. ```python import threading class ExportClient: _instance = None _lock = threading.Lock() def __new__(cls, timeout=5.0): with cls._lock: if cls.__dict__.get("_instance") is None: obj = super().__new__(cls) obj.timeout = timeout cls._instance = obj return cls._instance ``` **Fix three: make `__init__` idempotent.** Guard the body with a flag set on first run (`if getattr(self, "_ready", False): return`). It works, but it leaves a class whose arguments are silently ignored on every call after the first — an API that quietly discards what the caller asked for. Raising instead, when a later call passes arguments that contradict the configured ones, is usually more honest than ignoring them. **Three details that separate a strong answer.** First, **thread safety**: `if cls._instance is None` followed by an assignment is two separate steps, and the interpreter may switch threads between them, so two callers can each create an instance. A `threading.Lock` around the check-and-create, or simply creating the object at import time, closes that window; the GIL does not, because it guarantees nothing about a sequence of operations. Second, **subclasses**: `cls._instance` resolves through the class hierarchy, so once the base has an instance, every subclass call finds it and returns an object of the wrong class. Reading `cls.__dict__.get("_instance")` — or keying a dictionary by `cls` — gives each class its own slot. Third, **reconstruction paths**: copying and unpickling rebuild an object with `cls.__new__(cls)` and no call arguments, so a `__new__` that requires them raises there, and any invariant the pattern claims to enforce has to survive those paths too. **The strategic point.** A singleton is process-global mutable state. It makes tests order- dependent, hides a dependency that would be obvious as a parameter, and — as the export example shows — turns a configuration mistake into an intermittent runtime failure far from its cause. Reaching for it because "there should only be one" is usually the wrong reason; passing the one object explicitly to whatever needs it costs a parameter and removes the whole class of bug.
- Why is `if cls._instance is None` followed by an assignment unsafe across threads, even with the GIL?Because the check and the assignment are separate operations and the interpreter may switch threads between them. Two callers can both see `None`, both create an object, and the second assignment wins — leaving two supposedly-unique instances alive, with the loser's state silently orphaned. The GIL serializes individual bytecode execution, not sequences of it. Take a `threading.Lock` around the check-and-create, or create the object at import time so there is no window at all.
- A subclass of your singleton returns the base class's instance. What went wrong?`cls._instance` is looked up through the class hierarchy, so once the base class has stored an instance, a subclass call finds that attribute, sees it is not `None`, and returns an object of the wrong class. Read the slot with `cls.__dict__.get('_instance')` so each class only sees its own, or keep a dictionary keyed by `cls`. It is also a fair moment to ask whether a subclassable singleton is a coherent idea at all.
- When would you avoid the `__new__` singleton entirely?Almost always. A module-level instance or a factory function expresses one-shared-object without making `Client()` lie about what it does, and it lets tests substitute a different object by passing it in. The `__new__` version hides a process-global dependency inside construction, makes test order matter, and breaks the ordinary expectation that calling a class gives you a new object.
saying these in an interview costs you the question
- Assumes the initializer is skipped for a cached instance
- Puts expensive one-time setup in a singleton's __init__
- Believes the GIL makes check-then-assign atomic
- Stores the instance slot on the base so subclasses share it
- Treats a singleton as the default answer to shared state
- Ignores later constructor arguments without telling the caller