PythonConstructors (__init__)

Constructors (__init__)

When you create a new instance of a class, Python automatically calls a special method named __init__ on it, if one is defined. This is the initializer — the place where you set up whatever instance attributes an object needs before it's ready to use. You never call __init__ directly yourself; Python invokes it for you the moment you write ClassName(...).

Python
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        print(f"Created a point at ({self.x}, {self.y})")

p = Point(3, 4)
print(p.x, p.y)
Created a point at (3, 4)
3 4

The arguments you pass to Point(3, 4) flow straight into __init__ as x and y, right after the automatically-supplied self. Inside the method, self.x = x and self.y = y store those values as attributes on the new object, so they're still there long after __init__ has finished running.

__init__ is not technically the constructor
It's extremely common — and a useful simplification while learning — to call `__init__` "the constructor." Strictly speaking, it isn't. Python actually creates the new, empty object using a different special method, `__new__`, and only afterward calls `__init__` on that already-existing object to initialize it. `__new__` is responsible for construction; `__init__` is responsible for initialization. In everyday Python, you almost never need to touch `__new__` — it's mostly relevant for advanced cases like customizing immutable types or implementing singletons — but it's worth knowing the distinction exists.

Python
class Point:
    def __new__(cls, *args, **kwargs):
        print("__new__ builds the object")
        return super().__new__(cls)

    def __init__(self, x, y):
        print("__init__ initializes the object")
        self.x = x
        self.y = y

p = Point(1, 2)
__new__ builds the object
__init__ initializes the object
Default parameter values in __init__

__init__ is a normal function under the hood, so it supports default parameter values exactly like any other function. This lets callers leave out arguments that usually have a sensible default, while still allowing them to override it when needed.

Python
class Book:
    def __init__(self, title, author, available=True):
        self.title = title
        self.author = author
        self.available = available

default_copy = Book("Dune", "Frank Herbert")
checked_out = Book("Dune", "Frank Herbert", available=False)

print(default_copy.available)
print(checked_out.available)
True
False

Most copies of a book start out available, so available=True as a default means most callers can simply write Book(title, author). Only the code that already knows a particular copy is checked out needs to pass available=False explicitly.

Calling other instance methods from __init__

Because self is fully available inside __init__, you can call any other method on the object while it's still being constructed — most commonly to validate the arguments before committing them to attributes. This keeps validation logic in one place instead of duplicating checks everywhere an object might be created.

Python
class Book:
    def __init__(self, title, author, pages, available=True):
        self.title = title
        self.author = author
        self.pages = pages
        self.available = available
        self._validate()

    def _validate(self):
        if self.pages <= 0:
            raise ValueError("A book must have at least 1 page")

good_book = Book("Dune", "Frank Herbert", 412)
print(good_book.pages)

bad_book = Book("Nothing", "Nobody", 0)
412
ValueError: A book must have at least 1 page

Here _validate is an ordinary instance method — the leading underscore is just a convention signaling "internal use," not a language rule. __init__ sets the attributes first, then calls self._validate() to check them, and _validate raises a ValueError if something is wrong. Because this happens inside __init__, an invalid Book can never be fully created — the exception interrupts construction before the object is handed back to the caller.

  • __init__ runs automatically right after an object is created — you never call it directly.

  • Parameters after self can have default values, just like in any function, so callers can omit arguments that usually take a common value.

  • Any method — including validation helpers like _validate — can be called from inside __init__, since self already refers to the new object.

  • Raising an exception inside __init__ prevents the object from being successfully constructed at all.

Validate after assigning, or validate the raw arguments
In the example above, attributes are assigned before `_validate` is called, so validation reads `self.pages` rather than the raw `pages` parameter. Either approach works, but be consistent: if a method later assumes an object was fully validated during construction, make sure every code path through `__init__` actually performs that validation before returning.