The Constructor: Initializing Objects with __init__
Objects have a complete lifecycle: creation (via __init__), active use, and destruction (via __del__). Python calls these special methods automatically.
Try it: Classes, Instances and Attributes
How a class creates instances with __init__ and self, why an attribute read checks the instance's __dict__ before the class, why assigning through an instance never changes the class, how a class-level list is shared by every instance, and why obj.method(x) is Class.method(obj, x).
How it works
- Dog(...) makes a new, empty instance and calls Dog.__init__(new_object, ...); self.name = ... stores into that object's __dict__.
- Reading obj.attr looks in obj.__dict__ first, then in the class.
- Assigning obj.attr = value always writes obj.__dict__, so it shadows a class attribute for that one object only.
- Mutating a class-level list (self.tricks.append) changes the one list every instance shares.
- obj.method is found on the class and bound to obj, so obj.method(x) runs Class.method(obj, x).
Default run (30 steps): Ready: class Dog and the statements you picked. Watch where each attribute is found and where each assignment writes. … Finished. Printed: ['sit'] / Wolf / Canis familiaris / Fido is 5.
Simplified: The lab's own small Python interpreter runs a fixed, hand-written script (you choose the variant and the values, never the code); nothing is executed as real Python. Every script was run in CPython 3.12 with the same inputs and the lab reproduces its output and error messages. Object numbers are illustrative, not real id() values.
Loading the simulation…
A Complete Object Lifecycle
An object is not just a value that appears and disappears. It passes through a lifecycle: Python creates it, initializes it, lets your program use it, and eventually destroys it. The special method __init__ participates at the beginning of that lifecycle. If a class defines __del__, that method participates at the end.
When you create an instance, Python allocates memory for the object and calls its __init__ method automatically. The method gives the class a place to establish sensible starting values. After initialization, the object can be used through its methods and attributes. Later, when the object is no longer needed, Python destroys it and calls __del__ if the class defines that method.
Creation Before Use
A class defines the behavior and structure from which instances are created. An instance is one individual object made from that class. When an instance is created, Python automatically calls __init__. You do not normally call __init__ yourself to begin the object's lifecycle.
In this example, User is the class and user is an instance. When User("Mina") creates the instance, Python calls __init__ automatically. The self parameter identifies that particular instance. The assignments create instance attributes named name and active, so this instance begins with name set to "Mina" and active set to True.
Building Initial State
The main job of __init__ is to make an instance ready for predictable use. An instance attribute is data attached to one particular object. Writing self.name = name attaches the value from the parameter name to the current object identified by self. Because each instance receives its own call to __init__, each instance has its own separate set of initialized attributes.
Initialize every attribute that the object will use inside __init__. This helps every instance begin in a valid and predictable state instead of requiring later code to guess whether an attribute exists.
Destruction Triggers
What do you think happens?
A variable holds an object. The variable is then reassigned to 42. What lifecycle event should you expect for the original object?
Reveal answer
Its __del__ method may be called as the object is destroyed.
Variable reassignment can make the original object no longer needed. At that destruction moment, Python calls __del__ if the class defines it.
The destructor __del__ is called when an object is about to be destroyed. The source identifies three common triggers: a variable holding the object is reassigned, a variable goes out of scope, or the program terminates. In each case, Python decides that the object is no longer needed and gives __del__ a final opportunity to perform cleanup.
created first
destroyed firstThe first assignment creates a Marker instance, so __init__ runs and stores the label. The second assignment replaces the object held by marker with the value 42. The original Marker object is then no longer needed in this simple sequence, so Python destroys it and calls __del__.
Cleanup Responsibilities
Most Python objects do not need a destructor. Python's garbage collector handles memory automatically when objects are no longer in use. A destructor becomes useful when an object manages an external resource, such as an open file handle, a database connection, or a network socket.
This example illustrates the intended division of responsibility: __init__ prepares the object and any resources it needs, while __del__ provides a final chance to release resources before the object vanishes. You cannot stop the object from being destroyed. You can only use __del__ for cleanup at that final moment.
Mistakes with Initialization and Destruction
Common mistake Treating __init__ as a normal method that must be called manually.
What it looks likeuser = User("Mina") user.__init__("Mina")Why it failsPython already calls
__init__when the instance is created. Calling it again repeats initialization and is not needed for ordinary object creation.Do this insteadCreate the instance and let Python invoke __init__ automatically.
Common mistake Assigning to a local name instead of an instance attribute.
What it looks likedef __init__(self, name): name = nameWhy it failsThis does not attach the value to the object. The instance needs an assignment using self, such as
self.name= name.Do this insteadUse self.attribute_name when storing data on the current instance.
Common mistake Assuming all instances share one set of instance attributes.
What it looks likefirst = User("Mina") second = User("Omar")Why it failsEach instance receives its own call to
__init__and its own separate set of attributes.Do this insteadThink of the class as the shared definition and each instance as an individual object with independently initialized attributes.
Common mistake Adding __del__ to every class without managing an external resource.
Why it failsMost objects do not need explicit cleanup because Python automatically manages memory.
Do this insteadUse __del__ when the object manages an external resource such as a file handle, database connection, or network socket.
Trace the Lifecycle
Write a class named Counter with an __init__ method that stores a starting number in an instance attribute named value. Then add a __del__ method that announces the value during cleanup. Create one Counter instance, use a second variable to refer to it, and reassign the first variable. Trace which event happens at creation and which event happens when the original object is no longer needed.
Hints
- The first parameter of both special methods should identify the current instance.
- Store the constructor's starting number with an assignment that begins with self.
- Reassignment matters only when the original object is no longer held by the relevant variables.
Following Two Instances
A class creates two objects, first and second. Each object initializes its own label. The variable first is then reassigned. Which object is affected by that reassignment?
- 1Create first
Python creates the first instance and calls
__init__, so the instance receives its own initialized label. - 2Create second
Python creates a separate second instance and calls
__init__again. This instance receives an independent set of attributes. - 3Reassign first
The name first no longer refers to the original first object. That original object may be destroyed if no relevant variable still holds it.
- 4Keep tracing second
Reassigning first does not change the attributes belonging to second. It is a separate instance.
Each object was initialized separately. Reassigning first can trigger destruction of the original first object, while the second object remains active.
Key Takeaways
- 1
__init__runs automatically when Python creates an instance. - 2The self parameter identifies the particular instance being initialized.
- 3Assignments such as
self.name= name create or set instance attributes. - 4Each instance has its own separately initialized attributes.
- 5
__del__runs when an object is about to be destroyed, including after reassignment, when a variable leaves scope, or when the program ends. - 6Most objects do not need
__del__; it is mainly useful for final cleanup of external resources.
At a glance
__init__is automatically called during object instantiation to establish the object's initial state.- self refers to the current instance, and
self.attributeassignments store data on that instance. - Every instance receives its own initialization and its own separate instance attributes.
__del__is called when Python is about to destroy an object, including after variable reassignment, scope exit, or program termination.- Destructors are uncommon because Python manages memory automatically; they are mainly useful for cleanup involving external resources.