LearningPython TutorialClasses & OOP
CHAPTER 16 · OBJECT-ORIENTED PROGRAMMING

Python Classes & OOP

Object-oriented programming related data aur behavior ko ek structured object me organize karne ka way hai. Is chapter me classes, objects, constructors, attributes, methods, encapsulation aur composition ko practical examples ke saath samjhenge.

English + Hinglish72 min readOnline practice included

What is object-oriented programming?

OOP me program ko objects ke around model kiya jata hai. Object ke paas state (data) aur behavior (methods) ho sakte hain. Python multi-paradigm language hai, so har problem ko class me convert karna zaroori nahi.

Hinglish Explanation

Student ko sirf dictionary se represent kar sakte ho, lekin jab student ke data ke saath methods bhi chahiye — jaise average calculate karna ya result show karna — class useful ho sakti hai.

Class and object

A class is a blueprint/type definition. An object is an instance created from that class.

basic-class.pyPython
class Student:
    pass

student_one = Student()
student_two = Student()

print(type(student_one))
print(student_one is student_two)

Both objects same class ke instances hain, but separate objects hain.

__init__ and object initialization

__init__ instance creation ke immediately after initialization ke liye commonly used special method hai.

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

student = Student("Aman", "Python")
print(student.name)
print(student.course)

self.name and self.course instance attributes hain. Har instance apni values rakh sakta hai.

Try first class →

What is self?

self current instance ko refer karta hai. Ye reserved keyword nahi hai, but Python convention strongly self naam use karti hai. Instance method call me Python instance ko first argument ke roop me pass karta hai.

self.pyPython
class Greeter:
    def greet(self):
        print("Hello from", self)

g = Greeter()
g.greet()

Instance methods

Instance method object ke state ko read ya modify kar sakta hai.

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

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("amount must be positive")
        self.balance += amount

    def show_balance(self):
        return self.balance

account = BankAccount("Riya", 1000)
account.deposit(500)
print(account.show_balance())

Method ke through state update karne se validation ek central place par rakhi ja sakti hai.

Instance attributes vs class attributes

attributes.pyPython
class Student:
    platform = "BrounStack"

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

first = Student("Aman")
second = Student("Riya")

print(first.name, first.platform)
print(second.name, second.platform)

name instance-specific hai. platform class attribute hai and class namespace se shared/default lookup hota hai.

Important: Mutable class attributes, such as a shared list, all instances ke beech unintentionally share ho sakte hain. Per-instance mutable data usually __init__ me create karo.

Mutable class attribute gotcha

shared-list.pyPython
class WrongCart:
    items = []

    def add(self, item):
        self.items.append(item)

one = WrongCart()
two = WrongCart()
one.add("Book")
print(two.items)  # same shared list

Correct approach: self.items = [] inside __init__.

Validate state inside methods

validation.pyPython
class Student:
    def __init__(self, name, score):
        self.name = name
        self.set_score(score)

    def set_score(self, score):
        if not 0 <= score <= 100:
            raise ValueError("score must be 0 to 100")
        self.score = score

student = Student("Kabir", 88)
print(student.score)

Object ko valid state me rakhna class design ka important goal hai.

Encapsulation in Python

Python strict private fields ko Java/C++ style enforce nahi karta. Naming conventions intent communicate karti hain:

  • name — public attribute.
  • _name — internal-use convention; external code ko caution signal.
  • __name — name mangling trigger karta hai; true security/privacy boundary nahi.
encapsulation.pyPython
class Account:
    def __init__(self, balance):
        self._balance = balance

    def get_balance(self):
        return self._balance

Python culture often unnecessary getters/setters se bachti hai; simple public attributes fine ho sakte hain until validation/computation needed ho.

Properties for controlled attribute access

property.pyPython
class Student:
    def __init__(self, score):
        self.score = score

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if not 0 <= value <= 100:
            raise ValueError("score must be 0 to 100")
        self._score = value

student = Student(90)
student.score = 95
print(student.score)

Property method logic ko normal attribute syntax ke peeche expose kar sakti hai. Use only when it improves API clarity.

Class methods

@classmethod first argument me class receive karta hai, conventionally cls. Alternative constructors ek common use case hain.

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

    @classmethod
    def from_text(cls, text):
        name, score = text.split(",")
        return cls(name.strip(), float(score))

student = Student.from_text("Aman,88")
print(student.name, student.score)

Static methods

@staticmethod automatic self ya cls receive nahi karta. It can be useful for helper logic conceptually related to the class, but many helpers ordinary module functions bhi ho sakte hain.

staticmethod.pyPython
class Grade:
    @staticmethod
    def is_valid(score):
        return 0 <= score <= 100

print(Grade.is_valid(85))

Composition — objects inside objects

Composition means one object another object ko contain/use karta hai. “Has-a” relationship ke liye useful hai.

composition.pyPython
class Address:
    def __init__(self, city):
        self.city = city

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

address = Address("Lucknow")
student = Student("Riya", address)
print(student.name, student.address.city)

Composition often small focused classes ko combine karke flexible design deta hai.

Try composition →

OOP concepts — where they fit

  • Encapsulation: related state and behavior ko class me organize karna, valid state protect karna.
  • Abstraction: user ko simple interface dena while implementation details hide karna.
  • Inheritance: one class another class se behavior reuse/extend kare — Chapter 17 me detail.
  • Polymorphism: different objects same interface ko support kar sakte hain — Chapter 17 me practical examples.

String representation preview

Default object print usually memory-style representation dikhata hai. Special methods such as __str__ and __repr__ readable/debug representations define kar sakte hain; detailed data-model discussion Chapter 17 me hogi.

str-preview.pyPython
class Student:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return f"Student: {self.name}"

print(Student("Aman"))

When should you use a class?

  • Data and related behavior naturally belong together.
  • Multiple similar objects create karne hain.
  • Object ko valid state maintain karna hai.
  • Domain concept ko clear interface dena hai.
  • Composition/reusable behavior se design easier hota hai.

Simple transformation, one-off script, ya stateless calculation ke liye function/dictionary sometimes simpler and better choice hai.

Practical example — StudentResult

student-result.pyPython
class StudentResult:
    def __init__(self, name, marks):
        self.name = name
        self.marks = list(marks)

    def average(self):
        if not self.marks:
            return 0
        return sum(self.marks) / len(self.marks)

    def grade(self):
        score = self.average()
        if score >= 90:
            return "A"
        if score >= 75:
            return "B"
        if score >= 60:
            return "C"
        return "D"

result = StudentResult("Aman", [82, 91, 76])
print(f"{result.name}: {result.average():.2f} ({result.grade()})")

Constructor data initialize karta hai; methods calculations ko object ke behavior ke roop me organize karte hain.

Common beginner mistakes

  • Method definition me self bhool jana.
  • self.name = name ki jagah sirf local name variable banana.
  • Har small function ko unnecessary class me convert karna.
  • Mutable class attribute ko per-instance data samajhna.
  • Class aur instance attribute difference ignore karna.
  • Object state ko invalid values me freely chhod dena.
  • __init__ se value return karne ki koshish karna.
  • __private ko security mechanism samajhna.
  • Getter/setter boilerplate copy karna jab simple attribute enough ho.
  • Inheritance ko composition se pehle default choice banana.

Beginner best practices

  • Class names ke liye PascalCase use karo.
  • Methods and attributes ke liye clear snake_case names use karo.
  • Class ko one clear responsibility ke around design karo.
  • Constructor ko predictable rakho; heavy side effects avoid karo.
  • Invalid state ko early validate karo.
  • Simple design prefer karo; abstraction only when useful ho.
  • Composition ko seriously consider karo before deep inheritance.
  • Public interface clear rakho and internal details unnecessarily expose mat karo.

Chapter checklist

  • Class aur object ka difference clear hai?
  • __init__ aur self ka role samajh aaya?
  • Instance attributes and methods bana sakte ho?
  • Class vs instance attributes ka difference clear hai?
  • Mutable class attribute risk samajh aaya?
  • Property se validation implement kar sakte ho?
  • @classmethod and @staticmethod ka basic use pata hai?
  • Composition ka “has-a” relationship samajh aaya?

Practice Task — Course Enrollment Manager

Classes use karke small enrollment system banao.

  1. Course class banao with title and maximum seats.
  2. Student class banao with name and email.
  3. Enrollment class banao jo student and course objects contain kare.
  4. Course me enrolled-students collection per instance rakho.
  5. enroll() method banao.
  6. Duplicate enrollment prevent karo.
  7. Maximum seats validate karo.
  8. Invalid seat count par ValueError raise karo.
  9. available_seats() method banao.
  10. One class attribute add karo, e.g. platform name.
  11. One property use karke validated field expose karo.
  12. One @classmethod alternative constructor try karo.
  13. Readable __str__ preview add karo.
  14. At least two students and one course ke saath test karo.

Open practice starter →