You have been using types this whole time.
Strings. Integers. Lists. Dictionaries. Every one of those is a type. And they don't just hold data. They also come with built-in behaviors. Strings have .upper(). Lists have .append(). Dictionaries have .get().
Now here is the question. What if the type you need doesn't exist yet? What if you want a Student type that holds a name, age, and scores, and also knows how to calculate its own average?
That is exactly what classes let you build.
The Cookie Cutter Idea
A class is a blueprint. It describes what something looks like and what it can do. By itself it's just a description, it doesn't actually exist yet.
An object is what you get when you use the blueprint to create something real. You can create as many objects from one class as you want. Same structure, different data.
Cookie cutter is the class. The cookies are the objects.
Your First Class
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
class Student: defines a new type called Student.
def __init__ is a special method that runs automatically when you create a new Student. Think of it as the setup instructions.
self refers to the specific object being created. When you make a Student named Alex, self is that Alex object. When you make a Student named Priya, self is that Priya object. Same class, different self.
self.name = name stores the name on this specific object. Without self., the value disappears when __init__ finishes.
Now create some students.
student1 = Student("Alex", 25)
student2 = Student("Priya", 22)
print(student1.name)
print(student2.name)
print(student1.age)
Output:
Alex
Priya
25
Two objects. Same blueprint. Different data.
Adding Methods
Methods are functions that belong to a class. They define what the object can do.
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def greet(self):
print(f"Hi, I'm {self.name} and I'm {self.age} years old.")
def birthday(self):
self.age = self.age + 1
print(f"Happy birthday {self.name}! You are now {self.age}.")
student1 = Student("Alex", 25)
student1.greet()
student1.birthday()
student1.greet()
Output:
Hi, I'm Alex and I'm 25 years old.
Happy birthday Alex! You are now 26.
Hi, I'm Alex and I'm 26 years old.
birthday() changed self.age on that specific object. student1 is now 26. Any other Student object you created is still whatever age they were. The change only affected student1.
A More Real Example
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner
self.balance = balance
def deposit(self, amount):
self.balance = self.balance + amount
print(f"Deposited {amount}. New balance: {self.balance}")
def withdraw(self, amount):
if amount > self.balance:
print("Not enough money.")
else:
self.balance = self.balance - amount
print(f"Withdrew {amount}. New balance: {self.balance}")
def show_balance(self):
print(f"{self.owner}'s balance: {self.balance}")
account = BankAccount("Alex", 1000)
account.show_balance()
account.deposit(500)
account.withdraw(200)
account.withdraw(2000)
Output:
Alex's balance: 1000
Deposited 500. New balance: 1500
Withdrew 200. New balance: 1300
Not enough money.
Notice how the object remembers its state between method calls. self.balance keeps updating. Each call to deposit or withdraw works with the current balance, not some starting value. That persistent memory is what makes objects powerful.
Multiple Objects, Independent State
account1 = BankAccount("Alex", 1000)
account2 = BankAccount("Priya", 5000)
account1.deposit(200)
account2.withdraw(1000)
account1.show_balance()
account2.show_balance()
Output:
Deposited 200. New balance: 1200
Withdrew 1000. New balance: 4000
Alex's balance: 1200
Priya's balance: 4000
Two completely separate accounts. Operations on one never touch the other. That independence is the whole point of objects.
The str Method
When you print an object, Python normally shows something ugly like <__main__.Student object at 0x10b3f2a90>. You can fix that.
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"Student: {self.name}, Age: {self.age}"
student1 = Student("Alex", 25)
print(student1)
Output:
Student: Alex, Age: 25
__str__ is another special method like __init__. Python calls it automatically whenever it needs to display your object as text. Return a string from it and that's what gets printed.
Three Things That Confuse Everyone
Forgetting self in method definitions.
class Student:
def __init__(name, age): # missing self
self.name = name
TypeError: Student.__init__() takes 2 positional arguments but 3 were given
Every method in a class must have self as the first parameter. Every single one. Even if you don't use self inside the method. Python passes the object automatically as the first argument, so self has to be there to receive it.
Forgetting self when storing data.
def __init__(self, name):
name = name # wrong, this just creates a local variable
def __init__(self, name):
self.name = name # correct, this stores it on the object
Without self., the data exists only while __init__ is running and then vanishes. Always use self. to store anything you need to keep.
Using the class itself instead of creating an object.
Student.greet() # wrong, Student is the blueprint
student1.greet() # correct, student1 is the actual object
Try This
Create classes_practice.py.
Build a class called Product for an online store.
It should store: name, price, and quantity in stock.
It should have these methods:
-
show_info()that prints all the product details neatly -
sell(quantity)that reduces the stock by that amount, but prints "not enough stock" if the quantity requested is more than what's available -
restock(quantity)that adds to the stock -
apply_discount(percent)that reduces the price by that percentage and prints the new price
Create two different products. Call different methods on each. Make sure the state changes correctly and the two products stay independent.
What's Next
Objects are great for organizing data and behavior together. But Python also needs to work with data that lives in files, not just in memory. Next post is about reading and writing files, which is how your programs start talking to the real world.
Top comments (0)