Quick answer: __init__() is the initializer that runs after Python creates an instance. It receives self, prepares instance attributes, and validates starting state. __new__() is the method that creates the object, while __init__.py is a package file with a different purpose. Keep those three names distinct when explaining object construction and imports.

In Python, __init__() is the initializer method that usually runs right after an object is created. It receives the new instance as self and prepares that instance for normal use.
Python programmers often call __init__() a constructor because it is the method people write when they want setup code for a class. More precisely, object creation is handled by __new__(), while __init__() fills in the instance attributes and checks starting inputs.
The official Python documentation for object.__init__ explains the data model rule. The Python classes tutorial gives broader class background, and the packages tutorial plus the regular packages reference explain the separate __init__.py file used by packages.
The two names are easy to mix up: __init__() is a method inside a class, while __init__.py is a file inside a package directory. They are related by name, but they solve different problems.
Use __init__() when each instance needs its own starting data. Keep it focused on setup. If the method opens network connections, reads large files, or does slow work, creating the object becomes surprising and hard to test.
A clean initializer also documents what the class expects at the moment an instance is born. Readers can scan its parameters, see which attributes are created, and understand the minimum data needed for a useful object. That makes later methods easier to follow because the basic object shape is established in one predictable place. Python has one __init__ method per class, so Python Multiple Constructors Guide uses default arguments and class methods to provide alternate construction paths.
Create An Instance With __init__
A class can define __init__() to accept inputs and store them on self.
class Profile:
def __init__(self, name, role):
self.name = name
self.role = role
def label(self):
return f"{self.name}: {self.role}"
profile = Profile("Ada", "admin")
print(profile.label())
Calling Profile("Ada", "admin") creates a new Profile instance and passes the arguments to __init__() after self. You do not pass self yourself.
Attributes such as self.name belong to the instance. A second profile can have different values without changing the first profile.
Validate Starting Input
__init__() is a good place for small, direct input normalization that every instance should have.
class Temperature:
def __init__(self, celsius):
self.celsius = float(celsius)
def fahrenheit(self):
return self.celsius * 9 / 5 + 32
reading = Temperature("21.5")
print(reading.celsius)
print(round(reading.fahrenheit(), 1))
This class accepts a string or number and stores a float. The rest of the class can then work with a predictable value.
Keep validation close to the class when all instances must follow the same rule. For heavier parsing, use a class method, factory function, or a separate parser so object setup stays readable.

Do Not Return A Value
__init__() must return None. If it returns another object, Python raises TypeError.
class BadSetup:
def __init__(self):
return "ready"
try:
BadSetup()
except TypeError as error:
print(type(error).__name__)
The purpose of __init__() is to update the instance that already exists. It is not a replacement for a function that computes and returns a result.
If construction needs to choose a different class or reuse an existing object, that belongs in __new__() or a factory function, not in __init__().
Handle Optional Starting Data
When an instance should start with a list, dictionary, or set, build that container inside __init__(). This keeps each instance independent.
class ReadingLog:
def __init__(self, owner, books=None):
self.owner = owner
self.books = list(books or [])
def add(self, title):
self.books.append(title)
log = ReadingLog("Ada")
log.add("Fluent Python")
print(log.books)
The books=None default avoids sharing one list across every instance. A fresh list is created for each ReadingLog unless the caller provides starting titles.
This pattern is useful for carts, logs, queues, tags, and other per-object collections. The class owns its starting data, and tests can create a clean instance whenever they need one.

Call Parent Setup With super()
In an inheritance chain, a subclass usually calls super().__init__() so the parent class can run its setup first.
class Account:
def __init__(self, email):
self.email = email
class AdminAccount(Account):
def __init__(self, email, level):
super().__init__(email)
self.level = level
admin = AdminAccount("[email protected]", 3)
print(admin.email, admin.level)
Without the super() call, AdminAccount would skip the setup that stores email. That can leave inherited methods with missing attributes.
Keep subclass setup additive when possible: let the parent prepare the shared part, then let the subclass add its own fields. This style is easier to extend than copying parent setup into every child class.
Understand __init__.py In Packages
__init__.py is a package file, not a class initializer. It tells Python that a directory is a regular package and can also expose a small public package API.
from pathlib import Path
from tempfile import TemporaryDirectory
import importlib
import sys
with TemporaryDirectory() as root:
package = Path(root) / "demo_pkg"
package.mkdir()
(package / "__init__.py").write_text("from .helpers import greet\n", encoding="utf-8")
(package / "helpers.py").write_text(
"def greet(name):\n return f'Hi, {name}'\n",
encoding="utf-8",
)
sys.path.insert(0, root)
try:
demo_pkg = importlib.import_module("demo_pkg")
print(demo_pkg.greet("Ada"))
finally:
sys.path.remove(root)
sys.modules.pop("demo_pkg", None)
sys.modules.pop("demo_pkg.helpers", None)
Here __init__.py re-exports greet, so users can call demo_pkg.greet(). In larger packages, keep this file small and predictable. Avoid slow imports or broad side effects during package import.
The practical rule is simple: use __init__() inside classes to prepare each instance, use __init__.py inside package directories to define package import behavior, and keep both forms focused on lightweight setup.

Separate __new__ From __init__
For normal classes, Python creates an instance and then calls __init__ to configure it. __new__ matters when customizing creation, especially for immutable subclasses, so most application classes only need __init__.
class User:
def __new__(cls, name):
instance = super().__new__(cls)
print("created")
return instance
def __init__(self, name):
self.name = name
print("initialized")
print(User("Ada").name)
Validate State At The Boundary
An initializer is a good place to reject impossible values before the object is used. Raise a specific exception and leave the instance in a valid state rather than accepting a value that later methods cannot handle.
class Port:
def __init__(self, value):
if not 1 <= value <= 65535:
raise ValueError("port is outside the valid range")
self.value = value
print(Port(8000).value)

Use Optional Defaults Deliberately
For optional state, choose a default that cannot be confused with a real value, and avoid mutable defaults in the function signature. Create a new list or dictionary per instance.
class Cart:
def __init__(self, items=None):
self.items = list(items) if items is not None else []
first = Cart()
second = Cart()
first.items.append("book")
print(first.items, second.items)
Call A Base Initializer With super
When a subclass extends a base class that has initialization work, call super().__init__ with the required arguments. This keeps the base portion of the instance configured instead of silently skipping it.
class Account:
def __init__(self, owner):
self.owner = owner
class PremiumAccount(Account):
def __init__(self, owner, points=0):
super().__init__(owner)
self.points = points
print(PremiumAccount("Ada", 10).__dict__)
The Python data model explains the object.__init__ and __new__ relationship, and the class tutorial shows normal instance initialization. Related references include super() and __new__().
For related object construction and state, compare super(), __new__(), and vars() when deciding where initialization and inspection belong.
Frequently Asked Questions
What does __init__() do in Python?
__init__() runs after an instance is created and initializes its attributes or validates the arguments passed to the class constructor.
Is __init__() the same as a constructor?
It is commonly called a constructor, but technically __new__() creates the instance and __init__() initializes the already-created instance.
Can __init__() return a value?
No. __init__() must return None; returning another value raises TypeError during object construction.
What is the difference between __init__ and __init__.py?
__init__ is a class method for instance initialization, while __init__.py is a package file used for package structure and import behavior.