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(...).
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.
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.
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.
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
selfcan 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__, sinceselfalready refers to the new object.Raising an exception inside
__init__prevents the object from being successfully constructed at all.