Constructors (__init__), self, & Attributes
In previous lessons, we created an empty object and then manually assigned attributes like s1.name = "Sai". But what if you forget to assign name to s2? Your app crashes!
In this lesson, we will explore Constructors (__init__), self, and Attributes to automatically initialize objects cleanly on creation.
1. Learning Objective
By the end of this lesson, you will understand:
- Why manual attribute assignment is dangerous and how
__init__fixes it. - What the magic method
__init__actually does (initialization, NOT creation). - What
selfreally is (the memory reference of the newly created object). - How
self.name = nameworks step-by-step in memory.
2. Why This Concept Exists
Imagine a smartphone factory. If workers built a phone shell and shipped it without installing a screen or battery, customers would receive broken, empty devices.
Without constructors, every time you create an object in Python, you have to remember to type:
s1 = Student()
s1.name = "Sai"
s1.age = 21
s1.course = "Python"
If another developer creates s2 = Student() and forgets s2.course, the app crashes later with an AttributeError.
A Constructor (__init__) is an automatic initialization routine that runs the instant an object is created, guaranteeing that every object starts life with all mandatory attributes properly populated.
3. Common Beginner Confusions
Confusion 1: "I think __init__ creates the object in memory."
- Answer: FALSE!
__init__does NOT create the object. Python's internal__new__()method creates the empty object in Heap RAM first. Then Python passes that empty object into__init__so it can populate attributes.__init__is an initializer, not a creator.
Confusion 2: "What is self? Is it a Python keyword?"
- Answer:
selfis NOT a reserved keyword in Python; it is simply a parameter convention. When Python calls__init__, it automatically passes the memory address reference of the new object as the first parameter. By convention, we name this first parameterself.
4. Mental Model: Factory Assembly Line Tag
Imagine a smartphone factory line:
- Empty Chassis Built (Object Memory Creation):
- The machine stamps out a raw phone chassis at physical station
#Station-99.
- The machine stamps out a raw phone chassis at physical station
- Attaching the Factory Tag (
self):- The worker clips a tag onto
#Station-99that readsself.
- The worker clips a tag onto
- Customizing Attributes (
self.color = "Black"):- The worker installs a black shell onto this specific phone (
self.color). - When the next chassis
#Station-100comes down the line,selfautomatically points to#Station-100.
- The worker installs a black shell onto this specific phone (
5. Internal Python Execution Flow
What actually happens when you write s1 = Student("Sai", 21)? Here is the exact internal 9-step execution trace:
Code Executed: s1 = Student("Sai", 21)
↓ Execution Trace:
1. Python reads Student("Sai", 21).
2. Python allocates a fresh empty block of Heap Memory (e.g., Address 0x9000).
3. Python calls the class constructor __init__().
4. Python passes Heap Address 0x9000 as the first argument -> self = 0x9000.
5. Python passes "Sai" as the second argument -> name = "Sai".
6. Python passes 21 as the third argument -> age = 21.
7. Inside __init__, self.name = name executes -> stores "Sai" inside 0x9000.
8. Inside __init__, self.age = age executes -> stores 21 inside 0x9000.
9. __init__ finishes. Python returns reference 0x9000 and assigns it to variable s1.
6. Memory Visualization: Constructor Execution
RAM Layout during init Execution
init LOCAL STACK FRAME
➔
HEAP MEMORY (Address 0x9000)
7. Python Code Implementation
class Student:
# Constructor Method
def __init__(self, name, age):
# Assigning instance attributes using self
self.name = name # Left: Object attribute | Right: Parameter value
self.age = age
# Creating two distinct student objects
s1 = Student("Sai", 21)
s2 = Student("Ananya", 20)
print(f"s1 -> Name: {s1.name}, Age: {s1.age}")
print(f"s2 -> Name: {s2.name}, Age: {s2.age}")
Output:
s1 -> Name: Sai, Age: 21
s2 -> Name: Ananya, Age: 20
8. Line-by-Line Execution Analysis
Let's dissect the line self.name = name inside __init__:
Right Side: = name
- What Python does: Looks up the local parameter variable
namepassed into the function (e.g.,"Sai").
Left Side: self.name
- What Python does: Takes the reference
self(Address0x9000), navigates to that Heap object, and creates an internal attribute key namedname.
Assignment: self.name = name
- Result: Stores string
"Sai"inside attribute keynameon Heap object0x9000.
9. Before / After Memory Visualization
Before __init__ Runs
- Heap Address
0x9000: An uninitialized empty object container{}.
After __init__ Completes
- Heap Address
0x9000: A populated object container{"name": "Sai", "age": 21}.
10. Illustration Prompt for Diagram Generation
Excalidraw Diagram Prompt: A detailed 3-step hand-drawn workflow illustrating Python init and self. Step 1 (Creation): Show an arrow from "Student('Sai', 21)" creating a box in Heap RAM labeled "Empty Object 0x9000". Step 2 (Self Binding): Draw a function box labeled "init(self, name, age)". Draw a curved purple line pointing from "self" directly to "Empty Object 0x9000". Step 3 (Attribute Assignment): Draw two green arrows putting "name = 'Sai'" and "age = 21" inside object 0x9000. White background, clean hand-drawn style, color scheme: Purple (self/stack), Green (Heap Object), Orange (Data values).
Quick Summary
__init__is an automatic initialization function that runs immediately after an object is created in memory.selfholds the memory address reference of the object currently being initialized.self.attribute_name = valueattaches data directly to the object in Heap RAM.- Constructors guarantee that every object is instantiated with mandatory attributes.