Python Classes and Objects Explained for Beginners (Complete OOP Guide)

 

 Python Classes and Objects – Object-Oriented Programming for Beginners

📅 Published on Code with Py📂 Category: Python OOP⏱️ Reading time: ~15 min

You have written Python programs with variablesloops, and functions. Now it is time to think differently — to stop writing instructions one by one and start building things. Object-Oriented Programming (OOP) is how every real software in the world is built — from Instagram to Gmail to your favourite game. Python makes OOP beginner-friendly with a clean syntax. This guide teaches you classes and objects from absolute zero, with real-life traces, analogies, and working code.

📋 Table of Contents

  1. What is Object-Oriented Programming?
  2. The Real-Life Analogy — Blueprint and Building
  3. Your First Class in Python
  4. The __init__ Method – Giving Objects Their Starting Values
  5. Step-by-Step Trace — What Happens When You Create an Object
  6. The self Parameter — What It Actually Means
  7. Instance Attributes vs Class Attributes
  8. Methods — Functions That Belong to a Class
  9. Multiple Objects From One Class
  10. Procedural vs OOP — A Side-by-Side Comparison
  11. Real-Life Projects Using Classes
  12. Practice Problems
  13. FAQ
  14. Summary

1. What is Object-Oriented Programming?

Before OOP existed, programmers wrote code as a long sequence of instructions — do this, then do that, then do this again. That works for small programs. But imagine writing Instagram with that approach. You would drown in thousands of disconnected lines of code.

Object-Oriented Programming is a way of organising your code by grouping related data and behaviour together into something called an object. Instead of thinking "what should happen next?", you think "what things exist in my program and what can each thing do?"

Every object in Python has two parts — attributes (data it holds) and methods (things it can do). A student has a name and marks (attributes) and can introduce themselves or calculate their grade (methods).

Procedural Thinking

Write steps in order. Store data in separate variables. Functions work on any data. Gets messy as program grows.

OOP Thinking

Group data + behaviour together. Create objects that know their own data. Clean and scalable for large programs.


2. The Real-Life Analogy — Blueprint and Building

🏠 Think of a Class as an Architect's Blueprint

An architect draws one blueprint for a flat. Using that single blueprint, the builder constructs 50 identical flats in a building. Each flat is built from the same design but is a separate, independent unit — one has red walls, one has blue walls, one has a dog.

In Python — the blueprint is a Class and each individual flat is an Object. You define a class once and create as many objects as you need from it. Every object is independent but follows the same structure.

One more everyday example — think about your smartphone. Every phone of the same model has the same features (camera, battery, screen). Those features are defined in the class. Your specific phone — with your contacts, photos, and wallpaper — is an object. Same structure, unique data.


3. Your First Class in Python

A class is created using the class keyword — one of Python's reserved keywords. The class body is indented just like a function body.

# Define a simple class
class Student:
    pass   # empty class for now

# Create an object from the class
s1 = Student()

print(s1)
print(type(s1))
<__main__.Student object at 0x7f1234abcd>
<class '__main__.Student'>
The weird-looking output Student object at 0x7f... just means Python created an object and stored it at that memory address. You will never need to use that address — you use the variable name s1 instead.

Add data to an object manually

class Student:
    pass

s1 = Student()
s1.name  = "Rohan"
s1.marks = 85
s1.city  = "Bengaluru"

print(s1.name)
print(s1.marks)
print(s1.city)
Rohan
85
Bengaluru
Manually adding attributes to objects after creation works but is messy and error-prone. If you forget to set one attribute, your code will crash later with an AttributeError. The correct solution is __init__ — explained next.

4. The __init__ Method – Giving Objects Their Starting Values

The __init__ method (short for initialise) is a special method Python calls automatically the moment you create a new object. You put all the starting data for the object inside this method. This guarantees every object starts with the exact attributes it needs — no forgetting, no missing data.

Think of __init__ as the registration form you fill out when you join a new school. The moment you enroll, you must provide your name, age, and class. No student gets in without filling it.

class Student:
    def __init__(self, name, marks, city):
        self.name  = name
        self.marks = marks
        self.city  = city

# Create objects — values go in automatically
s1 = Student("Rohan", 85, "Bengaluru")
s2 = Student("Priya", 92, "Mumbai")

print(s1.name,  s1.marks, s1.city)
print(s2.name,  s2.marks, s2.city)
Rohan 85 Bengaluru
Priya 92 Mumbai

5. Step-by-Step Trace — What Happens When You Create an Object

This is the part most tutorials skip. Let us trace exactly what Python does when you write s1 = Student("Rohan", 85, "Bengaluru")

Trace of: s1 = Student("Rohan", 85, "Bengaluru")
1
Python sees Student(...) — looks up the class named Student in memory
2
Python allocates a new empty object in memory — a blank Student
3
Python automatically calls __init__(self, "Rohan", 85, "Bengaluru")
4
self points to the new empty object created in step 2
5
self.name = "Rohan" → stores "Rohan" inside this object
6
self.marks = 85 → stores 85 inside this object
7
self.city = "Bengaluru" → stores "Bengaluru" inside this object
8
__init__ finishes → the fully built object is assigned to variable s1
s1 now holds a Student object with name="Rohan", marks=85, city="Bengaluru"

6. The self Parameter — What It Actually Means

self is the most confusing thing for beginners in OOP. Here is the clearest way to think about it.

When you create two Student objects — s1 and s2 — both share the same class definition. When Python calls s1.greet(), it needs to know which student's name to print. That is what self does — it is a reference to the specific object the method was called on.

class Student:
    def __init__(self, name):
        self.name = name

    def greet(self):
        print(f"Hello, my name is {self.name}.")

s1 = Student("Rohan")
s2 = Student("Priya")

s1.greet()   # self = s1, so self.name = "Rohan"
s2.greet()   # self = s2, so self.name = "Priya"
Hello, my name is Rohan.
Hello, my name is Priya.

🪪 self is like your ID card in a crowd

Imagine 50 students in a room. A teacher shouts "Show your mark sheet." Each student shows their own mark sheet — not someone else's. In Python, when greet() runs, self is the student's ID card — it tells Python "use THIS student's data, not any other."

self is always the first parameter of every method inside a class. Python fills it in automatically when you call the method — you never pass it manually. The name "self" is a convention, not a rule — technically you could name it anything, but never do that in real code.

7. Instance Attributes vs Class Attributes

Attributes in Python classes come in two varieties. Understanding the difference prevents confusing bugs.

class Student:
    # Class attribute — shared by ALL objects
    school = "Code with Py Academy"

    def __init__(self, name, marks):
        # Instance attributes — unique to each object
        self.name  = name
        self.marks = marks

s1 = Student("Rohan", 85)
s2 = Student("Priya", 92)

print(s1.name,  s1.school)
print(s2.name,  s2.school)

# Change class attribute — affects ALL objects
Student.school = "Python High School"
print(s1.school)
print(s2.school)
Rohan Code with Py Academy
Priya Code with Py Academy
Python High School
Python High School
FeatureInstance AttributeClass Attribute
Defined inInside __init__ using selfDirectly in class body
Unique per object?Yes — every object has its own copyNo — shared by all objects
Change affectsOnly that one objectAll objects (if changed via class name)
Exampleself.name = "Rohan"school = "ABC School"

8. Methods — Functions That Belong to a Class

A method is a function defined inside a class. Methods define what an object can do. Just like a person can eat, sleep, and work — an object can have multiple methods that perform different actions using its own data.

class Student:
    def __init__(self, name, marks):
        self.name  = name
        self.marks = marks

    def introduce(self):
        print(f"Hi! I am {self.name}.")

    def get_grade(self):
        if   self.marks >= 90: return "A"
        elif self.marks >= 75: return "B"
        elif self.marks >= 50: return "C"
        else:                   return "Fail"

    def is_pass(self):
        return self.marks >= 40

    def report(self):
        grade  = self.get_grade()
        status = "Pass" if self.is_pass() else "Fail"
        print(f"{self.name:10} | Marks: {self.marks} | Grade: {grade} | {status}")

# Test all methods
s1 = Student("Rohan", 85)
s2 = Student("Arjun", 33)

s1.introduce()
s2.introduce()
s1.report()
s2.report()
Hi! I am Rohan.
Hi! I am Arjun.
Rohan      | Marks: 85 | Grade: B | Pass
Arjun      | Marks: 33 | Grade: Fail | Fail

Method calling another method — step-by-step trace

Trace of: s1.report() where s1 = Student("Rohan", 85)
1
s1.report() is called → self = s1 object
2
Inside report(), calls self.get_grade() → runs get_grade with self = s1
3
self.marks = 85 → 85 >= 75 is True → returns "B"
4
Back in report(), grade = "B"
5
Calls self.is_pass() → 85 >= 40 is True → returns True
6
status = "Pass"
Prints: "Rohan | Marks: 85 | Grade: B | Pass"

9. Multiple Objects From One Class

One of the biggest powers of classes is creating many independent objects from a single definition. Each object lives in its own memory space and does not affect the others.

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner   = owner
        self.balance = balance

    def deposit(self, amount):
        self.balance += amount
        print(f"{self.owner}: Deposited ₹{amount}. Balance = ₹{self.balance}")

    def withdraw(self, amount):
        if amount > self.balance:
            print(f"{self.owner}: Insufficient balance!")
        else:
            self.balance -= amount
            print(f"{self.owner}: Withdrew ₹{amount}. Balance = ₹{self.balance}")

    def show_balance(self):
        print(f"{self.owner}'s balance: ₹{self.balance}")

# Three independent accounts from ONE class
acc1 = BankAccount("Rohan",  5000)
acc2 = BankAccount("Priya",  12000)
acc3 = BankAccount("Arjun")

acc1.deposit(3000)
acc2.withdraw(4000)
acc3.deposit(1000)
acc3.withdraw(5000)

print()
acc1.show_balance()
acc2.show_balance()
acc3.show_balance()
Rohan: Deposited ₹3000. Balance = ₹8000
Priya: Withdrew ₹4000. Balance = ₹8000
Arjun: Deposited ₹1000. Balance = ₹1000
Arjun: Insufficient balance!

Rohan's balance: ₹8000
Priya's balance: ₹8000
Arjun's balance: ₹1000
Notice that depositing into acc1 did not affect acc2 or acc3. Each object is fully independent — they share the class blueprint but own their data. This is the core power of OOP.

10. Procedural vs OOP — Side-by-Side Comparison

# Procedural style — student data in separate variables
name1, marks1 = "Rohan", 85
name2, marks2 = "Priya", 92

def get_grade(marks):
    if marks >= 90:   return "A"
    elif marks >= 75: return "B"
    else:              return "C"

print(name1, get_grade(marks1))
print(name2, get_grade(marks2))
Rohan B
Priya A
# OOP style — data and logic bundled together
class Student:
    def __init__(self, name, marks):
        self.name  = name
        self.marks = marks

    def get_grade(self):
        if   self.marks >= 90: return "A"
        elif self.marks >= 75: return "B"
        else:                   return "C"

s1 = Student("Rohan", 85)
s2 = Student("Priya", 92)
print(s1.name, s1.get_grade())
print(s2.name, s2.get_grade())
Rohan B
Priya A
FeatureProceduralOOP
Code organisationFunctions and variables separateBundled in classes
Adding a new studentMore variables, pass to every functionCreate one new object
ScalabilityGets messy beyond 100 linesScales cleanly to 100,000 lines
ReusabilityFunctions must be duplicatedOne class, unlimited objects
Real-world matchHard to model real thingsNatural — everything is an object

11. Real-Life Projects Using Classes

Project 1 – Library Book Management System

class Book:
    def __init__(self, title, author, copies):
        self.title   = title
        self.author  = author
        self.copies  = copies
        self.issued  = 0

    def issue(self, member):
        if self.copies > self.issued:
            self.issued += 1
            print(f"'{self.title}' issued to {member}.")
        else:
            print(f"'{self.title}' is not available right now.")

    def return_book(self, member):
        if self.issued > 0:
            self.issued -= 1
            print(f"'{self.title}' returned by {member}. Thanks!")

    def available(self):
        print(f"'{self.title}' — {self.copies - self.issued} of {self.copies} copies available.")

b1 = Book("Python Basics", "Code with Py", 3)
b1.available()
b1.issue("Rohan")
b1.issue("Priya")
b1.issue("Arjun")
b1.issue("Meera")  # No copies left
b1.return_book("Rohan")
b1.available()
'Python Basics' — 3 of 3 copies available.
'Python Basics' issued to Rohan.
'Python Basics' issued to Priya.
'Python Basics' issued to Arjun.
'Python Basics' is not available right now.
'Python Basics' returned by Rohan. Thanks!
'Python Basics' — 1 of 3 copies available.

Project 2 – Employee Salary Calculator

class Employee:
    def __init__(self, name, role, base_salary):
        self.name        = name
        self.role        = role
        self.base_salary = base_salary

    def bonus(self):
        rates = {"Manager": 0.20, "Developer": 0.15, "Intern": 0.05}
        return self.base_salary * rates.get(self.role, 0.10)

    def total_salary(self):
        return self.base_salary + self.bonus()

    def payslip(self):
        print(f"\n--- Pay Slip: {self.name} ---")
        print(f"Role:         {self.role}")
        print(f"Base Salary:  ₹{self.base_salary:,.0f}")
        print(f"Bonus:        ₹{self.bonus():,.0f}")
        print(f"Total Salary: ₹{self.total_salary():,.0f}")

e1 = Employee("Rohan",  "Manager",   80000)
e2 = Employee("Priya",  "Developer", 60000)
e3 = Employee("Arjun",  "Intern",    15000)

e1.payslip()
e2.payslip()
e3.payslip()
--- Pay Slip: Rohan ---
Role:         Manager
Base Salary:  ₹80,000
Bonus:        ₹16,000
Total Salary: ₹96,000

--- Pay Slip: Priya ---
Role:         Developer
Base Salary:  ₹60,000
Bonus:        ₹9,000
Total Salary: ₹69,000

--- Pay Slip: Arjun ---
Role:         Intern
Base Salary:  ₹15,000
Bonus:        ₹750
Total Salary: ₹15,750

12. Practice Problems

Beginner level

  1. Create a Car class with brand, model, year attributes
  2. Add a method that prints the car's full info
  3. Create a Circle class with radius — add area() and perimeter() methods
  4. Create 3 different car objects and print each one
  5. Add a class attribute for the fuel type (Petrol/EV)

Intermediate level

  1. Create a Temperature class — store in Celsius, convert to Fahrenheit and Kelvin
  2. Build a Counter class with increment(), decrement(), reset() methods
  3. Create a Rectangle class with area(), perimeter() and is_square() methods
  4. Build a ShoppingCart class — add items, remove items, show total
  5. Create a Password class that checks strength (weak/medium/strong)

Real-life projects

  1. Build a Student class with grading system
  2. Build a BankAccount class with deposit and withdraw
  3. Create a Library system with Book class
  4. Build a Hospital class that manages patient records
  5. Create a simple Inventory system for a shop

Think deeper

  1. What happens if two objects share the same class attribute?
  2. Can a method return another object of the same class?
  3. What is the difference between self.x and Class.x?
  4. Can you have a method with no parameters except self?
  5. How many objects can you create from one class?
For every practice problem, first identify — what are the attributes (data)? What are the methods (actions)? Write those down before writing any code. This thinking habit is what professional developers do.

13. FAQ

Q1. What is the difference between a class and an object?

A class is the blueprint — it defines what attributes and methods something will have. An object is a real, working instance built from that blueprint. You define a class once but create as many objects as you need from it. For example, Student is the class — s1 = Student("Rohan", 85) is the object.

Q2. Why does __init__ always have self as the first parameter?

Every method in a class needs a way to know which specific object it is working with. self is that reference — it points to the current object. Python passes it automatically when you call the method on an object. Without self, the method would have no way to access or modify the object's data.

Q3. Can I create a class without __init__?

Yes. If you don't define __init__, Python uses a default one that does nothing. Your objects will still be created successfully but they will have no starting attributes. You would then have to set attributes manually on each object after creation — which is less safe and less clean.

Q4. What is the difference between a method and a regular function?

A regular function is standalone — it is not connected to any data. A method is a function defined inside a class — it always has access to the object's data through self. Methods are called on objects using dot notation: s1.greet().

Q5. Can one object's changes affect another object of the same class?

No — for instance attributes. Each object has its own separate copy of all instance attributes. Changing s1.name never affects s2.name. However, class attributes are shared — changing a class attribute through the class name affects all objects that have not overridden it.

Q6. Is everything in Python an object?

Yes — this is one of Python's core principles. Integers, strings, lists, functions, and even classes themselves are all objects in Python. Every value you use has attributes and methods. That is why you can call "hello".upper() or [1,2,3].append(4) — strings and lists are objects of their respective classes.


✅ Quick Summary — What You Learned

  • class is a blueprint that defines attributes and methods
  • An object is a real instance built from a class — each is independent
  • __init__ runs automatically when an object is created — sets starting values
  • self refers to the current object — Python fills it in automatically
  • Instance attributes are unique per object — defined with self.x inside __init__
  • Class attributes are shared by all objects — defined directly in the class body
  • Methods are functions inside a class — they access object data through self
  • OOP scales cleanly — one class, unlimited objects, easy to maintain

"In OOP, you stop writing instructions and start building things — objects that think, remember, and act on their own."

— Code with Py

Found this post useful? Share it with a friend who is learning Python. Drop your projects in the comments — let's build together!

Comments

Popular posts from this blog

Python Strings – A Complete Guide for Beginners (with Examples)

Python Control Flow Statements – Complete Guide with Real-Life Examples

Python Loops Explained – for Loop and while Loop with Examples and Output