How do Python's `__new__` and `__init__` differ during object construction?
answer
- Construction is two steps, not one
- One step creates, the other configures
- First argument is the class, not the instance
- No decorator, yet still a static method
- Only __new__'s result reaches the caller
basics
~20 snew creates and returns the instance and takes the class as its first argument; init receives that already-created instance and only sets it up. Calling the class runs new first, then init on its result.
solid answer
~40 s`MyClass(a)` goes through `type.__call__`, which calls `MyClass.__new__(MyClass, a)` to obtain an object and then, **only if that object is an instance of the class**, calls `__init__(obj, a)` on it. So `__new__` is the allocator and the decision point about *which* object exists; `__init__` is setup on an object that already exists. `__new__` is implicitly a static method — no `@staticmethod` needed — which is why it takes `cls` explicitly and why a subclass writes `super().__new__(cls)`. Both receive the same call arguments, so their signatures must be compatible. Override `__init__` for ordinary attribute setup, which is almost always; reach for `__new__` only when you must control creation itself: subclassing an immutable type, interning or caching instances, or returning an object of a different class.
code
python · 11 linesclass Trace:
def __new__(cls, *args, **kwargs):
print("__new__ ->", cls.__name__, args)
return super().__new__(cls)
def __init__(self, label):
print("__init__ ->", label)
self.label = label
Trace("export")go deeper
Recall that construction has two steps and that the one you normally write is __init__, which configures an object someone else already made. Know that __new__ exists, takes the class first, and produces the instance.
Explain type.__call__ end to end: __new__ with cls, the isinstance guard, then __init__ with the same arguments. Be ready to write a correct super().__new__(cls) and to say why forwarding extra arguments to it raises TypeError.
Show judgement about when creation control is worth it — immutable subclasses, interning, subclass dispatch — and name the costs you take on: repeated initialization for cached instances, copy and unpickle paths that call cls.__new__(cls), and harder subclassing.
Own the guidance for a codebase: keep construction total and boring by default, push creation-time policy into explicit factories rather than into __new__, and treat a custom __new__ as an API commitment that every subclass and serialization path must keep honouring.
Python splits object construction into two named steps, and most interview confusion comes from calling the second one "the constructor". The call `MyClass(a, b)` dispatches to the type of `MyClass` — its metaclass, normally `type` — so what actually runs is `type.__call__`, which behaves like this: ```python obj = MyClass.__new__(MyClass, a, b) if isinstance(obj, MyClass): type(obj).__init__(obj, a, b) return obj ``` **`__new__` is the creator.** It receives the class as its first argument, conventionally named `cls`, and it is responsible for producing an object and returning it. The base implementation, `object.__new__`, is what actually allocates the instance and gives it its type; a custom `__new__` almost always ends by delegating to it with `return super().__new__(cls)` and then, if needed, adjusting or replacing the result. Whatever `__new__` returns is what the caller gets — the protocol offers no later opportunity to substitute a different object. **`__init__` is the initializer.** It receives an already-created instance as `self` and exists purely for side effects on it: binding attributes, validating arguments, registering the object. Its own result is discarded and must be `None`. It runs after `__new__`, and it is skipped entirely when the object `__new__` returned is not an instance of the class being called — that is how a `__new__` that returns a completely foreign object bypasses initialization. **`__new__` is implicitly a static method.** Class creation special-cases the name: even though it is written as a plain `def` in the class body, it is wrapped as a static method automatically, so `@staticmethod` is unnecessary (and harmless). The practical consequence is that no class is bound for you — the class must be passed explicitly. That is why `cls` is an ordinary first parameter and why the delegation is `super().__new__(cls)` and not `super().__new__()`. A candidate who writes `def __new__(self, ...)` has usually not internalized that the first argument is the class about to be instantiated, which matters because a subclass call arrives with the *subclass* in `cls`; allocating with the hard-coded base class instead of `cls` silently breaks every subclass. **Both steps see the same arguments.** `type.__call__` forwards the call's positional and keyword arguments to both methods, so their signatures have to be compatible. Accepting `*args, **kwargs` in whichever one does not care about them is the usual way to keep that true. There is one asymmetry worth memorizing, because it is the single most common runtime error when people first write `__new__`: `object.__new__` accepts only the class. Writing `return super().__new__(cls, name)` raises `TypeError: object.__new__() takes exactly one argument (the type to instantiate)`. Arguments reaching `object.__new__` are tolerated *only* when `__new__` was not overridden and `__init__` was — the ordinary case — and `object.__init__` mirrors that rule in the other direction. If a class overrides neither and is called with arguments, the error is `takes no arguments`. **When to override which.** The default answer is `__init__`: it covers essentially all ordinary classes, and reaching for `__new__` to do attribute setup is a smell that makes subclassing and pickling harder for no gain. Override `__new__` when creation itself is the thing you need to control: * **Subclassing an immutable built-in** such as `int`, `str` or `tuple`. The value is fixed while the object is being made, so it must be handed to `int.__new__` / `str.__new__` / `tuple.__new__`; `__init__` arrives too late to change it. * **Interning, caching or pooling.** Returning an existing instance for a repeated key — the mechanism behind singletons and flyweights — is only possible where the object is chosen, and that is `__new__`. * **Returning a different class.** A base class can inspect its arguments and allocate a concrete subclass instead, giving dispatch at construction time. * **Returning a foreign object entirely**, which additionally suppresses `__init__`. **What the split costs.** A custom `__new__` becomes part of the class's contract with the rest of the runtime. Copying and unpickling rebuild objects by calling `cls.__new__(cls)` directly rather than by calling the class, so a `__new__` with required parameters raises there unless the class supplies the arguments through `__getnewargs__`. Caching in `__new__` means `__init__` may run repeatedly on the same object. And error handling is subtler: an exception in `__new__` means no object exists at all, while an exception in `__init__` means an allocated object is abandoned half-built. The compact framing to give an interviewer: `__new__` decides *which object exists* and returns it; `__init__` decides *what that object contains* and returns nothing. Everything else — the implicit static method, the explicit `cls`, the skipped initializer, the immutable-subclass rule — follows from that one distinction.
- Why does `__new__` work without an `@staticmethod` decorator?Class creation special-cases the name and wraps it as a static method automatically, so adding `@staticmethod` changes nothing. The visible consequence is that no class is bound for you: `cls` is an ordinary first parameter you must pass explicitly, which is why delegation reads `super().__new__(cls)`. It also means a subclass call arrives with the subclass in `cls`, so allocating with a hard-coded base class instead of `cls` quietly breaks every subclass.
- When does Python skip `__init__` after `__new__` has run?Whenever the object `__new__` returned is not an instance of the class that was called. `type.__call__` guards the initializer with an `isinstance` check, so returning a `list`, a string, or an object of an unrelated class means `__init__` never runs and that object is handed straight to the caller. A returned instance of a *subclass* still passes the check, so it is initialized normally.
- What breaks when a custom `__new__` requires constructor arguments?Machinery that rebuilds objects without going through the class call. Copying and unpickling reconstruct via `cls.__new__(cls)`, so a `__new__` with required parameters raises `TypeError` there unless the class supplies them through `__getnewargs__`. It is a good reason to keep required work in `__init__` and give `__new__` only the parameters it genuinely needs to decide which object exists.
__new__ is the factory floor that stamps out the chassis and decides which chassis you get; __init__ is the trim line that fits out whatever chassis arrives. The showroom hands over what the factory floor produced, no matter what the trim line thought.
saying these in an interview costs you the question
- Calls __init__ the constructor and treats __new__ as exotic
- Writes def __new__(self, ...) expecting an instance
- Forwards call arguments to object.__new__
- Claims __init__ always runs after __new__
- Insists __new__ needs the @staticmethod decorator
- Overrides __new__ for ordinary attribute assignment