Python Constructors
Table of Contents + −
In the last lesson you learned Classes and Objects. You saw how a class is a blueprint and an object is a real thing built from it. But there was a gap. Every object you made started out empty. You had to fill in its data one line at a time after creating it. That gets tiring fast. It is also easy to forget a piece. The constructor fixes that.
🤔 Why Constructors?
Say you are building a contacts app. Every person needs a name and an age the moment they exist. Without a constructor you create the object first. Then you set each field by hand.
This is the tiring way to give an object its starting data.
class Person: pass
alex = Person()alex.name = "Alex"alex.age = 30That works, but look at the problem. Nothing forces you to set name and age. You could create a person with no name at all and Python would not complain. On a small program you might catch it. On a big one you will not.
A constructor is a method that runs automatically the moment an object is created. Its job is to set that object’s starting data. So you give the values once, while making the object. That way you can never forget them.
🧩 What __init__ Is
In Python the constructor is a special method named __init__. The name has two underscores before it and two after. So people say “dunder init” out loud. Dunder just means double underscore.
You never call __init__ yourself. Python calls it for you. It happens automatically every time you create an object from the class.
Here is the same Person class, now with a constructor.
class Person: def __init__(self, name, age): self.name = name self.age = ageNow look at what changed. The class knows from the start that a person needs a name and an age. Let’s read it line by line.
def __init__(self, name, age):defines the constructor. It takes the new object first. That isself. Then it takes the values we want to store,nameandage.self.name = nametakes thenamevalue that was passed in and saves it onto the object.self.age = agedoes the same for the age.
So when the object is born, it already has its data. No empty objects.
🪞 What self Really Means
self is the one word that confuses everybody at first. So let’s slow down here.
When you create an object, Python makes a fresh empty object in memory. It hands that object to __init__ as the first argument. That object is self. It is the specific person being built right now.
Think of it like a hotel check-in. The form says “the guest” everywhere on it. When Alex checks in, “the guest” means Alex. When Riya checks in later, the same form now means Riya. self is that “the guest” word. It always points to whoever is being made at that moment.
So self.name = name reads as: “take the name that was passed in, and store it on this particular person.” The self.name on the left is the object’s own storage. The name on the right is the value that came in through the parentheses.
Note
You write self as the first parameter in __init__, but you do not pass it yourself. Python fills it in for you. You only pass the values that come after it.
🚀 Creating Objects With Values
Now the good part. To create a person, you call the class like a function and pass the values straight in.
This creates two different people. Each one has their own name and age.
class Person: def __init__(self, name, age): self.name = name self.age = age
alex = Person("Alex", 30)riya = Person("Riya", 25)
print(alex.name)print(alex.age)print(riya.name)print(riya.age)When this runs you get:
Output
Alex30Riya25See how the values you put inside the parentheses ended up inside each object? When you wrote Person("Alex", 30), Python created a fresh object. It set that object as self. Then it ran self.name = "Alex" and self.age = 30. The same thing happened again for Riya with her own values.
Here is the part that matters most. alex and riya are completely separate. Changing one never touches the other. Each object carries its own copy of the data.
This shows the values lining up with where they land:
| You write | Goes into self.name | Goes into self.age |
|---|---|---|
Person("Alex", 30) | "Alex" | 30 |
Person("Riya", 25) | "Riya" | 25 |
👋 A More Real Example
Let’s make it feel like an actual app. We will add a small method that uses the stored data to greet the person.
This class stores a name and age. Then it can introduce the person in a friendly line.
class Person: def __init__(self, name, age): self.name = name self.age = age
def introduce(self): print(f"Hi, I'm {self.name} and I'm {self.age} years old.")
alex = Person("Alex", 30)riya = Person("Riya", 22)
alex.introduce()riya.introduce()Running it prints a separate line for each person:
Output
Hi, I'm Alex and I'm 30 years old.Hi, I'm Riya and I'm 22 years old.Notice that introduce did not take a name or an age. It did not need to. The data was already saved on the object back when it was created. So introduce just reaches for self.name and self.age. That is the real payoff of a constructor. You set the data once, and every method afterwards can use it.
⚠️ Common Mistakes
A few mistakes catch almost everyone at first.
Forgetting self as the first parameter:
# ❌ Avoid: no self, so Python has nowhere to store the dataclass Person: def __init__(name, age): self.name = name
# ✅ Good: self comes first, alwaysclass Person: def __init__(self, name, age): self.name = nameForgetting to save the value onto self. Then it just disappears when the constructor ends:
# ❌ Avoid: name is received but never storedclass Person: def __init__(self, name, age): name = name
# ✅ Good: store it on the object with selfclass Person: def __init__(self, name, age): self.name = namePassing the wrong number of values. The constructor asks for a name and an age, so you must give both:
# ❌ Avoid: age is missingalex = Person("Alex")
# ✅ Good: give every value the constructor expectsalex = Person("Alex", 30)If you leave out a value you get an error like this:
Output
TypeError: Person.__init__() missing 1 required positional argument: 'age'✅ Best Practices
Some simple habits keep your constructors clean.
- Keep the parameter name and the attribute name the same.
self.name = namereads clearly and saves you from guessing later. - Set every piece of starting data inside
__init__, so an object is never half-built. - Keep the constructor focused on setting data. Avoid heavy work like reading files or calling the network there.
- Give parameters plain, honest names.
nameandagebeatnandaevery time.
🧩 What You’ve Learned
A quick recap before you move on.
- ✅ A constructor runs automatically the moment you create an object, and its job is to set the object’s starting data.
- ✅ In Python the constructor is the special method
__init__, said out loud as “dunder init”. - ✅
selfis the object being built right now, and Python passes it in for you so you never write it when creating the object. - ✅
self.name = namestores the value that came in through the parentheses onto that one object. - ✅ Each object keeps its own copy of the data, so two people made from the same class never share values.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What does Python do with the __init__ method?
Why: __init__ is the constructor, so Python calls it automatically the moment an object is created.
- 2
In def __init__(self, name, age), what does self refer to?
Why: self is the particular object being built at that moment, and Python passes it in automatically.
- 3
What does the line self.name = name do?
Why: The right side is the value passed in, and self.name on the left saves it onto that object.
- 4
How do you create a Person with the name Alex and age 30?
Why: You call the class like a function and pass the values; Python sends them into __init__ for you.
🚀 What’s Next?
You now know how to give an object its starting data the moment it is born. Next we will look closely at the data itself. We will see the values that live on each object and how they stay separate per object.