Constructors & __init__
Python actually splits object construction into two steps - __new__ creates the object, __init__ initializes it - and knowing the difference clears up a lot of confusion about what "the constructor" really is.
In everyday Python, __init__ is the method people call "the constructor," and for the vast majority of classes that's close enough. But __init__ doesn't actually create the object - it initializes an object that already exists. The true object-creation step is __new__, which __init__ follows.
__init__: Initialization, Not Creation
__init__ is called automatically after the object has already been created, with the new (already-existing) instance as self. Its job is to set up instance state - it must not return anything other than None.
By the time __init__ runs, p already exists as a Point instance; __init__ just fills in x and y.
Returning a non-None value from __init__ raises TypeError - it's a hard rule enforced by Python, reflecting that __init__'s only job is to mutate self, not to produce a value.
class Point:
def __init__(self, x: float, y: float) -> None:
self.x = x
self.y = y
p = Point(3, 4)
print(p.x, p.y) # 3 4The __new__ Distinction
__new__ is a static method (implicitly, even without the decorator - it's special-cased) responsible for actually allocating and returning a new instance. It receives the class (cls) as its first argument, not an instance, because the instance doesn't exist yet. object.__new__ provides the default implementation almost every class relies on implicitly.
__new__ runs first and returns the instance; that instance is then passed automatically as self to __init__.
class Traced:
def __new__(cls, *args, **kwargs):
print(f'__new__ called with cls={cls.__name__}')
instance = super().__new__(cls)
return instance
def __init__(self, value):
print(f'__init__ called with self={self!r}')
self.value = value
t = Traced(42)
# __new__ called with cls=Traced
# __init__ called with self=<...Traced object ...>Constructor Terminology
Strictly, "the constructor" is the combination of __new__ + __init__ working together (with __new__ doing the actual construction). In casual Python usage - and in most day-to-day code - people say "constructor" to mean __init__, since that's the method almost everyone overrides. This article uses "constructor" loosely to mean __init__ except where __new__ is explicitly discussed.
Object Creation Flow: Calling a Class
When you write Point(3, 4), Python's type.__call__ (the mechanism behind calling a class) does roughly this: call Point.__new__(Point, 3, 4) to get a new object; if the returned object is an instance of Point (or a subclass), call Point.__init__(obj, 3, 4) on it; then return the object.
Python calls __new__(cls, ...) to allocate/produce the instance.
If the result is an instance of cls (or a subclass), __init__(self, ...) is called on it automatically.
If __new__ returns something that is NOT an instance of cls, __init__ is skipped entirely.
The fully constructed object is returned from the call expression.
# Conceptually, Point(3, 4) is roughly:
obj = Point.__new__(Point, 3, 4)
if isinstance(obj, Point):
Point.__init__(obj, 3, 4)
result = objWhen to Override __new__
Almost all everyday classes only need to override __init__. Overriding __new__ is reserved for specialized cases: subclassing an immutable built-in type (like int or str, whose value must be fixed at creation time, before __init__ could set it), implementing the singleton pattern, or controlling which subclass gets instantiated.
int is immutable, so its underlying value must be fixed inside __new__ - by the time __init__ would run, it's too late to change the int's value.
class PositiveInt(int):
def __new__(cls, value):
if value <= 0:
raise ValueError('value must be positive')
return super().__new__(cls, value) # int's value must be set at creation time
p = PositiveInt(5)
print(p, type(p)) # 5 <class '__main__.PositiveInt'>
PositiveInt(-1) # raises ValueErrorDefault __init__ and Inheritance
If a class defines no __init__, Python uses the inherited one (ultimately object.__init__, which does nothing). When a subclass defines its own __init__, it does NOT automatically call the parent's - you must call super().__init__(...) explicitly if you want the parent's initialization to run too.
class Animal:
def __init__(self, name: str) -> None:
self.name = name
class Dog(Animal):
def __init__(self, name: str, breed: str) -> None:
super().__init__(name) # explicitly run Animal's init
self.breed = breed
d = Dog('Rex', 'Labrador')
print(d.name, d.breed) # Rex LabradorMultiple Instances, Independent State
Each call to a class creates a distinct object with its own __init__ run, so instance attributes never leak between instances (unless you deliberately share mutable state via a class attribute - see Classes & Objects).
p1 = Point(0, 0)
p2 = Point(1, 1)
p1.x = 99
print(p1.x, p2.x) # 99 1 - independentPractical Usage: Validation in __init__
A very common pattern is validating constructor arguments in __init__ and raising an exception immediately if they're invalid, so an object is never left in a half-valid state.
class Temperature:
def __init__(self, celsius: float) -> None:
if celsius < -273.15:
raise ValueError('temperature below absolute zero')
self.celsius = celsius
Temperature(-300) # raises ValueError before any instance is usableEdge Cases
If __new__ returns an instance of a different, unrelated class, __init__ is silently skipped for that returned object - a subtle trap when overriding __new__ for factory-like behavior.
class A:
def __new__(cls):
return object() # not an instance of A
def __init__(self):
print('init called') # never runs
a = A()
print(type(a)) # <class 'object'> - not A at all, and __init__ never printedKey Takeaways
__init__ initializes an object that already exists; it must return None.
__new__ is the actual object-creation step, receiving cls and returning the new instance; it runs before __init__.
Calling a class runs __new__ then, if the result is an instance of that class, __init__ automatically.
Override __new__ only for special cases: immutable-type subclassing, singletons, or controlling instantiation - override __init__ for ordinary initialization.
A subclass's __init__ does not automatically call its parent's __init__ - use super().__init__(...) explicitly.
class Point: def __init__(self, x, y): self.x = x self.y = y return self # TypeError: __init__ should return None class Point: def __init__(self, x, y): self.x = x self.y = y # implicitly returns None
__init__ must return None (or nothing); returning any other value raises TypeError, since __init__'s job is only to mutate self.
class Dog(Animal): def __init__(self, name, breed): self.breed = breed # Animal.__init__ never runs - self.name is never set class Dog(Animal): def __init__(self, name, breed): super().__init__(name) self.breed = breed
Defining __init__ in a subclass overrides the parent's entirely; call super().__init__(...) explicitly to run the parent's initialization too.
class Config(int): def __init__(self, value): self.value = value # too late - int's value was already fixed by __new__ class Config(int): def __new__(cls, value): return super().__new__(cls, value)
For immutable built-in types like int, str, and tuple, the value must be set in __new__, since by the time __init__ runs the immutable value is already fixed.
What is the difference between __new__ and __init__?
__new__ actually creates and returns the new object (receiving the class, cls, as its first argument); __init__ initializes an already-created object (receiving the instance, self) and must return None.
Why must __init__ return None?
Because __init__'s sole responsibility is to mutate the already-created instance in place; Python enforces this by raising TypeError if __init__ returns anything other than None.
In what order do __new__ and __init__ run when you call MyClass(...)?
__new__ runs first to create the instance; then, if the object __new__ returned is an instance of MyClass (or a subclass), __init__ is called automatically on it.
When would you need to override __new__ instead of just __init__?
When subclassing an immutable built-in type (whose value must be fixed at creation time), implementing a singleton pattern, or controlling which class/subclass actually gets instantiated - ordinary classes almost never need this.
Does a subclass's __init__ automatically call its parent class's __init__?
No - defining __init__ in a subclass completely overrides the parent's; you must explicitly call super().__init__(...) to also run the parent's initialization logic.
What happens to __init__ if __new__ returns an object that is not an instance of the class being constructed?
__init__ is skipped entirely for that object - Python only calls __init__ automatically when __new__'s return value is an instance of the class (or a subclass) being constructed.
Interview: why can't you validate/fix an int subclass's value inside __init__?
Because int is immutable - its underlying value is fixed at creation time inside __new__ (via object allocation), so by the time __init__ runs on the already-created instance, the value can no longer be changed; validation or value-setting for immutable types must happen in __new__.
Write a class Singleton that ensures only one instance is ever created, using __new__.
class Singleton: _instance = None def __new__(cls, *args, **kwargs): if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance a = Singleton() b = Singleton() print(a is b) # True
__new__ checks a class attribute holding the single allowed instance; if it already exists, it's returned instead of creating a new one, so every call to Singleton() yields the same object.
What does this print?
Derived init Derived.__init__ overrides Base.__init__ completely and does not call super().__init__(), so Base's initialization code never runs - only 'Derived init' prints.
Write a class ImmutablePoint subclassing tuple, overriding __new__ to store (x, y), with a __repr__ for readability.
class ImmutablePoint(tuple): def __new__(cls, x: float, y: float): return super().__new__(cls, (x, y)) def __repr__(self) -> str: return f'ImmutablePoint({self[0]}, {self[1]})' p = ImmutablePoint(1, 2) print(p) # ImmutablePoint(1, 2) print(tuple(p)) # (1, 2)
Since tuple is immutable, its contents must be supplied at creation time via __new__ rather than set afterward in __init__.
class Base:
def __init__(self):
print('Base init')
class Derived(Base):
def __init__(self):
print('Derived init')
Derived()