Course Content
Python Essentials for AI Engineer
6 sections · 48 lessons
What is the __init__ method?
What you need to know
What happens on Dataset("train.csv")
- Python calls
Dataset.__new__(Dataset, "train.csv"), which creates an empty instance. - Python calls
instance.__init__("train.csv"), which fills in the attributes. - The call returns the instance.
You almost never write __new__ yourself. You write __init__.
1class Dataset:2 def __init__(self, path, batch_size=32):3 if batch_size <= 0:4 raise ValueError(f"batch_size must be positive, got {batch_size}")5 self.path = path6 self.batch_size = batch_size78ds = Dataset("train.csv")9print(ds.path, ds.batch_size) # train.csv 3210try:11 Dataset("train.csv", batch_size=0)12except ValueError as e:13 print(e) # batch_size must be positive, got 0Validating in __init__ means an invalid object can never exist, so the rest of the class can trust its own attributes.
It must return None
1class Broken:2 def __init__(self):3 return 424try:5 Broken()6except TypeError as e:7 print(e) # __init__() should return None, not 'int'Subclasses and super().__init__
If a child class defines its own __init__, the parent's __init__ does not run automatically. Call super().__init__(...), or the parent's attributes will be missing.
Alternative constructors with @classmethod
A classmethod receives the class (cls) instead of an instance and can build instances in different ways — from_csv_row, from_config, from_path. This keeps __init__ simple and puts slow work where a test can avoid it.
A real-life example
A RAG service's retriever originally loaded a 2 GB vector index inside __init__. Every unit test that created a Retriever spent 40 seconds loading it, so the team stopped writing tests. The refactor passes the index in, and moves loading to a classmethod:
1class Retriever:2 def __init__(self, index, top_k=5):3 self.index = index # any object with a .search() method4 self.top_k = top_k56 @classmethod7 def from_path(cls, path, top_k=5):8 index = f"<index loaded from {path}>" # real code: a slow load from disk9 return cls(index, top_k)1011 def search(self, query):12 return self.index.search(query, self.top_k)1314class FakeIndex:15 def search(self, query, k):16 return [f"doc-{i}" for i in range(k)]1718r = Retriever(FakeIndex(), top_k=2) # tests: instant, no disk19print(r.search("refund policy")) # ['doc-0', 'doc-1']20prod = Retriever.from_path("/models/faq.index") # production path21print(prod.index) # <index loaded from /models/faq.index>Tests now run in milliseconds with a fake index, and production code still gets a one-line constructor.
Follow-up questions to expect
- "Is
__init__a constructor?" — Informally yes, but technically__new__constructs the object and__init__initialises it. - "What happens if
__init__returns a value?" — Python raisesTypeError: __init__() should return None. - "Can a class have several
__init__methods?" — No; a second definition replaces the first. Use default arguments or@classmethodalternative constructors.