Python Constructors

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 = 30

That 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 = age

Now 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 is self. Then it takes the values we want to store, name and age.
  • self.name = name takes the name value that was passed in and saves it onto the object.
  • self.age = age does 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

Alex
30
Riya
25

See 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 data
class Person:
def __init__(name, age):
self.name = name
# ✅ Good: self comes first, always
class Person:
def __init__(self, name, age):
self.name = name

Forgetting to save the value onto self. Then it just disappears when the constructor ends:

# ❌ Avoid: name is received but never stored
class Person:
def __init__(self, name, age):
name = name
# ✅ Good: store it on the object with self
class Person:
def __init__(self, name, age):
self.name = name

Passing the wrong number of values. The constructor asks for a name and an age, so you must give both:

# ❌ Avoid: age is missing
alex = Person("Alex")
# ✅ Good: give every value the constructor expects
alex = 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 = name reads 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. name and age beat n and a every 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”.
  • self is the object being built right now, and Python passes it in for you so you never write it when creating the object.
  • self.name = name stores 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. 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. 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. 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. 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.

Instance Variables

Share & Connect