Python __new__ vs __init__: Object Creation Explained
Comparison of Python's __new__ and __init__ methods covering call order, use cases, and common pitfalls.
Python __new__ vs __init__: Object Creation Explained
\n\nWhen you write MyClass() in Python, two dunder methods run behind the scenes: __new__ and __init__. Most Python developers only ever touch __init__, but understanding both unlocks powerful patterns like singletons, immutable subclasses, and factories.
The Core Difference
\n\n__new__ is the constructor. It creates the instance and returns the new object. It's a static method (implicitly) that receives the class itself as its first argument.
__init__ is the initializer. It configures an already-created instance and returns None. It receives the instance as self.
The Call Order
\n\nWhen Python evaluates MyClass(x, y), this happens:
- \n
__new__(cls, x, y)runs first and returns an instance. \n - If — and only if — the returned object is an instance of
cls, Python then calls__init__(instance, x, y). \n - If
__new__returns something that is not an instance ofcls,__init__is skipped entirely. \n
That last point is why factory patterns using __new__ work: return a different type and initialization is bypassed.
Signatures Side by Side
\n\nclass MyClass:\n def __new__(cls, *args, **kwargs):\n instance = super().__new__(cls)\n return instance\n\n def __init__(self, *args, **kwargs):\n self.args = args\n # implicit: return None\n\n\nNote that __new__ takes cls (the class), while __init__ takes self (the instance). Both usually receive the same trailing arguments passed to the constructor call.
When to Override __new__
\n\nFor regular classes, you almost never need __new__. Override it only in these cases:
1. Singleton Pattern
\nclass Singleton:\n _instance = None\n\n def __new__(cls):\n if cls._instance is None:\n cls._instance = super().__new__(cls)\n return cls._instance\n\nWatch out: __init__ still runs every time the class is called, so guard reinitialization there too.
2. Subclassing Immutable Types
\nTypes like int, str, tuple, and frozenset are immutable — their value is baked in at creation. You cannot mutate them in __init__, so customization must happen in __new__:
class PositiveInt(int):\n def __new__(cls, value):\n if value < 0:\n raise ValueError(\"Must be positive\")\n return super().__new__(cls, value)\n\n\n3. Metaclasses
\nA metaclass's __new__ creates the class object itself, letting you inject attributes, register subclasses, or rewrite methods before the class exists.
4. Factory Patterns
\nReturn a different subclass based on arguments — __init__ is skipped when the returned object isn't an instance of the requested class.
Common Mistakes
\n\nForgetting to Call super().__new__()
\nclass Broken:\n def __new__(cls):\n pass # returns None — no instance created!\n\nWithout super().__new__(cls), you never actually allocate an object. The call returns None, and __init__ is silently skipped.
Not Returning From __new__
\nAny code path in __new__ that forgets an explicit return returns None implicitly, breaking construction.
Passing Extra Args to object.__new__
\nobject.__new__ accepts only cls. If you also override __init__, forwarding extra arguments to super().__new__(cls, *args) raises TypeError in modern Python. Pass only cls unless the parent is an immutable builtin.
Assuming __init__ Runs Once
\nIn singleton patterns, __init__ re-runs on every call. Use a sentinel:
def __init__(self, value):\n if hasattr(self, \"_initialized\"):\n return\n self._initialized = True\n self.value = value\n\n\nQuick Comparison Table
\n\n| Aspect | __new__ | __init__ |
|---|---|---|
| Purpose | Create instance | Initialize instance |
| First arg | cls | self |
| Returns | New object | None |
| Runs when | Always | Only if __new__ returned cls instance |
| Override for | Singletons, immutables, metaclasses | Setting attributes on regular classes |
Bottom Line
\n\nReach for __init__ 99% of the time. Reach for __new__ when you need to control whether or which instance gets created — not just how it's configured.