OOP β Object-Oriented Programming
OOP β Object-Oriented Programming π―
Python ka most powerful paradigm β Object-Oriented Programming. Class, Object, self, Constructor, Instance vs Class variables, methods β sab kuch real-world examples ke saath. OOP samajh liya toh Python ka master ban gaye! Data Insights par complete deep dive.
π Is Chapter Mein Kya Sikhenge:
- π’ Basic: OOP Introduction, Class & Object, Constructor (__init__)
- π‘ Medium: Instance vs Class Variables, Instance Methods, self, Class & Static Methods
- π΄ Advanced (Part 10B): 4 Pillars, Encapsulation, Inheritance, Polymorphism, Abstraction, Dunder Methods, @property
1. OOP Introduction β What & Why π’
π Definition: Object-Oriented Programming (OOP) is a programming paradigm that organizes code around objects β instances of classes that combine data (attributes) and behavior (methods). Instead of writing separate functions and variables, OOP groups related data and functions together into classes, making code more organized, reusable, and easier to maintain. Python fully supports OOP β in fact, EVERYTHING in Python is an object!
π― Samjho Hinglish Mein: Procedural programming mein tum functions likhte ho β calculate_salary(), update_address(), send_email() β sab alag alag. OOP mein tum Employee class banate ho jisme yeh sab methods ho β emp.calculate_salary(), emp.update_address(). Data (name, salary, age) aur functions (calculate, update) ek jagah β encapsulated. Real world jaisa β har cheez ek "object" hai. Car ek object hai (color, model + start, brake methods). Employee ek object hai. Yeh soch code mein bhi lagaao β bahut clean aur maintainable hoti hai.
π OOP vs Procedural:
| Feature | Procedural | OOP |
|---|---|---|
| Structure | Functions + Variables (separate) | Classes with data + methods |
| Focus | Procedures (what to do) | Objects (what things are) |
| Data Security | Low (global variables exposed) | High (encapsulation) |
| Reusability | Limited | High (inheritance) |
| Scalability | Difficult for large projects | Excellent for large projects |
| Best For | Small scripts, simple tasks | Large applications, frameworks |
π Why OOP?
β’ Modularity β code chhote reusable units mein toota hua hota hai
β’ Reusability β ek class inheritance se multiple jagah use ho sakti hai
β’ Maintainability β bug fix karna easy, sab related code ek jagah
β’ Data Security β private attributes se data hide kar sakte ho
β’ Real-world modeling β real objects (Employee, Product, Car) directly code mein represent
β’ Industry Standard β Django, Flask, Pandas, all major Python libraries OOP par based
π¬ Interview Q&A:
Q: OOP kya hai aur kyun use karte hain?
Ans: Object-Oriented Programming ek paradigm hai jahan code ko OBJECTS ke around organize kiya jaata hai β jo classes ke instances hote hain. Har object mein DATA (attributes) aur BEHAVIOR (methods) hote hain β dono ek saath encapsulated. OOP use karte hain kyunki: (1) Modularity β code organized aur reusable, (2) Real-world modeling β real objects easily represent kar sakte hain, (3) Scalability β large projects manage karna easier, (4) Code reusability β inheritance se, (5) Data security β encapsulation se. Python fully OOP support karta hai β everything in Python is an object.
Q: Python mein "everything is an object" β iska kya matlab?
Ans: Python mein har cheez internally ek object hai β numbers, strings, lists, functions, even modules! type(10) β int, type("hi") β str, type([1,2]) β list. Har object apna type/class rakhta hai aur uske methods available hote hain β "hello".upper(), [1,2].append(3). Isliye Python fundamentally object-oriented language hai. Numbers bhi objects hain β (10).__add__(5) β 15 (yeh actually + operator karta hai internally).
2. Class & Object β Basic Structure π’
π Definition: A Class is a BLUEPRINT or template that defines what data and behaviors an object will have. An Object is an actual INSTANCE of a class β a concrete entity created from the blueprint. Classes are defined using the class keyword. Objects are created by "calling" the class like a function: obj = ClassName(). One class can create many objects β each with its own data.
π― Samjho Hinglish Mein: Class = blueprint. Object = actual thing built from blueprint. Example: Car class ek blueprint hai β batati hai car mein wheels, color, engine hote hain aur start/stop kar sakti hai. my_car = Car() ek object hai β actual Honda City with red color. Ek class se hazaar objects ban sakte hain, sab alag alag β jaise ek car blueprint se lakhs of cars manufacture hoti hain, sab different colors, features. Class = idea, Object = reality.
π Class Syntax:
Basic Syntax:
class ClassName:
# Attributes (data)
attribute = value
text
# Methods (functions inside class)
def method_name(self):
# method body
pass
Create object (instance)
object_name = ClassName()
Access attributes and methods
object_name.attribute
object_name.method_name()
Naming Convention:
β’ Class names: PascalCase (e.g., Employee, BankAccount)
β’ Object/attribute/method names: snake_case (e.g., employee_id, calculate_tax)
π» Examples:
# Example 1: Simplest Class class Dog: pass # Empty class (placeholder)
# Create objects (instances)
dog1 = Dog()
dog2 = Dog()
print(type(dog1)) #
print(dog1 == dog2) # False (different objects!)
# Both are Dog objects but separate instances
# Example 2: Class with attributes and method class Dog: # Class attribute (shared by all objects) species = "Canine" # Method (behavior) def bark(self):
print("Woof! Woof!") # Create objects my_dog = Dog() your_dog = Dog() # Access attribute print(my_dog.species) # Canine print(your_dog.species) # Canine (same for all) # Call method my_dog.bark() # Woof! Woof! your_dog.bark() # Woof! Woof!# Example 3: Real-world β Employee class class Employee: # Class attribute company = "Jatin Analytics" def work(self):
print("Employee is working...") def take_leave(self):
print("Employee is on leave") # Create objects emp1 = Employee() emp2 = Employee() # Add attributes to specific object (dynamic!) emp1.name = "Jatin" emp1.salary = 50000 emp2.name = "Priya" emp2.salary = 60000 # Each object has its own data print(f"{emp1.name}: βΉ{emp1.salary} @ {emp1.company}") print(f"{emp2.name}: βΉ{emp2.salary} @ {emp2.company}") # Jatin: βΉ50000 @ Jatin Analytics # Priya: βΉ60000 @ Jatin Analytics emp1.work() # Employee is working... emp2.take_leave() # Employee is on leaveemp1.name = "Jatin". Yeh flexible hai but risky β har object ke liye attributes yaad rakhne padte hain. Better way = Constructor (__init__) use karo β automatically initialize hote hain. Next topic mein detail mein sikhoge! π¬ Interview Q&A:
Q: Class aur Object mein kya difference hai?
Ans: Class ek BLUEPRINT ya template hai β batati hai object mein kya properties (attributes) aur behaviors (methods) honge. Class actual memory nahi leti β sirf definition hai. Object class ka INSTANCE hai β actual real-world entity jo memory mein exist karti hai. Class = "Car" ka blueprint. Object = "Honda City Red" β actual car. Ek class se multiple objects ban sakte hain β sabki apni alag data hoti hai. class Dog: definition hai, dog1 = Dog() object creation hai.
Q: Python class ka syntax kya hai?
Ans: class ClassName: keyword se class define hoti hai (colon important!). Class name PascalCase mein (Employee, BankAccount). Body indented hoti hai β attributes aur methods. Object banane ke liye: obj = ClassName(). Attribute access: obj.attribute. Method call: obj.method(). Empty class ke liye pass use karte hain. Methods ke first parameter hamesha self hota hai β jo current object ko refer karta hai.
3. Constructor β __init__() π’
π Definition: __init__() is a special method (called "dunder init" or constructor) that Python AUTOMATICALLY calls when a new object is created. It initializes the object's attributes with values. The first parameter is always self β which refers to the new object being created. Constructor eliminates the need to manually assign attributes after object creation.
π― Samjho Hinglish Mein: Constructor ek "birth certificate" hai object ka β jab bhi naya object banta hai, __init__ automatically call hota hai aur usko required data (name, age, etc.) ke saath initialize karta hai. Bina constructor ke tumhe manually har attribute assign karna padta hai. Constructor ke saath β Employee("Jatin", 25, 50000) β ek line mein object banega aur sab data set ho jaayega. Constructor Python interviews mein 100% puchha jaata hai!
π Syntax & Rules:
Syntax:
class ClassName:
def init(self, param1, param2, ...):
self.attribute1 = param1
self.attribute2 = param2
Creating object with constructor:
obj = ClassName(value1, value2)
Python automatically calls init(obj, value1, value2)
Rules:
β’ Name MUST be init (double underscore before & after)
β’ First parameter is ALWAYS self
β’ No explicit return statement
β’ Called automatically β never call manually!
β’ Can have default parameters
β’ Can accept *args and **kwargs
π» Examples:
# Example 1: Basic Constructor class Employee: def __init__(self, name, salary): self.name = name # Assign to object's attribute self.salary = salary
# Object create β automatically calls init
emp1 = Employee("Jatin", 50000)
emp2 = Employee("Priya", 60000)
print(emp1.name, emp1.salary) # Jatin 50000
print(emp2.name, emp2.salary) # Priya 60000
# Each object has ITS OWN name and salary β independent
# Example 2: Constructor with default values class Student:
def __init__(self, name, age, course="Data Analytics", is_active=True): self.name = name self.age = age self.course = course self.is_active = is_active def display(self): status = "Active" if self.is_active else "Inactive" print(f"{self.name} ({self.age}) β {self.course} β {status}") # Minimum required params only s1 = Student("Jatin", 25) s1.display() # Jatin (25) β Data Analytics β Active # Override defaults s2 = Student("Priya", 28, "Machine Learning", False) s2.display() # Priya (28) β Machine Learning β Inactive# Example 3: Constructor with validation & calculation class BankAccount:
def __init__(self, account_holder, initial_balance=0): # Validation inside constructor if initial_balance < 0: raise ValueError("Initial balance cannot be negative!") self.account_holder = account_holder self.balance = initial_balance self.transactions = [] # Empty list initialized print(f"β
Account created for {account_holder}") def deposit(self, amount): self.balance += amount self.transactions.append(f"Deposit: +βΉ{amount}") print(f"π° Deposited βΉ{amount} β New Balance: βΉ{self.balance}") # Create accounts acc1 = BankAccount("Jatin", 5000) acc2 = BankAccount("Priya") # Default balance 0 acc1.deposit(2000) acc2.deposit(10000) # Try invalid initialization # acc3 = BankAccount("Test", -500) β ValueError!return statement mat likho β __init__ automatically object return karta hai (behind the scenes). Explicit return karoge toh TypeError aayega. Constructor ka kaam sirf attributes initialize karna hai β value return nahi karna. π¬ Interview Q&A:
Q: __init__ kya hai aur kaise kaam karta hai?
Ans: __init__ Python ka CONSTRUCTOR method hai β jab bhi naya object create hota hai, Python AUTOMATICALLY __init__ call karta hai. Iska purpose object ki attributes ko initial values ke saath initialize karna hai. First parameter hamesha self hota hai β jo naye object ko refer karta hai. Baaki parameters constructor call ke time pass hote hain: Employee("Jatin", 50000) β internally Employee.__init__(new_obj, "Jatin", 50000) call hota hai. Bina __init__ ke tumhe manually har object mein attributes assign karne padte β very tedious.
Q: __init__ ko constructor kyun kehte hain jab return nahi karta?
Ans: Actually Python mein REAL constructor __new__ method hai β jo actual object CREATE karta hai in memory. __init__ technically INITIALIZER hai β jo already-created object mein data set karta hai. Lekin Python community __init__ ko constructor bolti hai casually kyunki practical developers ke liye yeh same job karta hai β new object aata hai to __init__ chalta hai. Interviews mein bata sakte ho ki technically __new__ constructor hai, __init__ initializer hai β but conventionally __init__ ko constructor bolte hain.
4. Instance Variables vs Class Variables π‘
π Definition: Instance Variables belong to a SPECIFIC OBJECT β each object has its own copy. They are defined inside __init__ using self.variable. Class Variables are SHARED across ALL objects of the class β defined directly inside the class body (outside any method). They are useful for constants or values that should be same for all instances.
π― Samjho Hinglish Mein: Instance variables = har object ki apni personal cheez. Jaise har employee ka apna naam, salary β sab alag alag. self.name mein "self" batata hai "current object ka name." Class variables = sab objects mein shared. Jaise sab employees ki company same hai β "Jatin Analytics." Ek jagah change karo, sab objects mein change. Rule: personal data = instance variable (self.xxx), common/shared data = class variable (ClassName.xxx).
π Comparison:
| Feature | Instance Variable | Class Variable |
|---|---|---|
| Where Defined | Inside __init__ (self.var) | Directly in class body |
| Belongs To | Individual object | Entire class (shared) |
| Access Via | obj.variable | ClassName.variable OR obj.variable |
| Copies Per Object | Separate copy for each | One copy shared by all |
| Change Effect | Only affects that object | Affects ALL objects |
| Use Case | Object-specific data (name, age) | Common data (company, constants, counter) |
π» Examples:
# Example 1: Class variable vs Instance variable class Employee: # Class variable (SHARED by all employees) company = "Jatin Analytics"
text
def __init__(self, name, salary):
# Instance variables (UNIQUE per employee)
self.name = name
self.salary = salary
emp1 = Employee("Jatin", 50000)
emp2 = Employee("Priya", 60000)
# Instance variables β each object different
print(emp1.name, emp1.salary) # Jatin 50000
print(emp2.name, emp2.salary) # Priya 60000
# Class variable β same for all
print(emp1.company) # Jatin Analytics
print(emp2.company) # Jatin Analytics
print(Employee.company) # Jatin Analytics (via class!)
# Example 2: Change class variable β affects all! class Employee: company = "Old Company" def __init__(self, name): self.name = name emp1 = Employee("Jatin") emp2 = Employee("Priya") print(emp1.company) # Old Company print(emp2.company) # Old Company # Change class variable β affects ALL objects Employee.company = "Jatin Analytics" print(emp1.company) # Jatin Analytics (changed!) print(emp2.company) # Jatin Analytics (changed!) # Change instance variable β only that object affected emp1.name = "Jatin Kumar" print(emp1.name) # Jatin Kumar print(emp2.name) # Priya (unchanged)# Example 3: Practical β Counter using class variable class Employee: # Class variable β counts total employees total_employees = 0 def __init__(self, name): self.name = name # Increment class variable when new object created Employee.total_employees += 1 self.emp_id = Employee.total_employees # Auto-assign ID # Create employees emp1 = Employee("Jatin") emp2 = Employee("Priya") emp3 = Employee("Rahul") # Each has unique auto-generated ID print(f"{emp1.name} - ID: {emp1.emp_id}") # Jatin - ID: 1 print(f"{emp2.name} - ID: {emp2.emp_id}") # Priya - ID: 2 print(f"{emp3.name} - ID: {emp3.emp_id}") # Rahul - ID: 3 # Total count print(f"Total Employees: {Employee.total_employees}") # Total Employees: 3obj.class_variable = value karo toh Python NEW instance variable banata hai us object mein β class variable UNCHANGED rehta hai! Class variable modify karne ke liye HAMESHA ClassName.variable = value use karo. Yeh subtle bug hai β carefully samjho! π¬ Interview Q&A:
Q: Instance variable aur Class variable mein kya difference hai?
Ans: Instance variable object-specific hai β har object ki apni alag copy hoti hai. __init__ ke andar self.variable = value se define hoti hai. Class variable class-wide shared hai β sab objects mein same copy. Class body mein directly define hoti hai (bina self ke). Instance variable personal data ke liye (name, age, balance). Class variable common data ke liye (company name, tax rate, counter). Access: instance var β sirf obj.var. Class var β ClassName.var OR obj.var. Modify karne mein farak β ClassName.var badal do toh sab objects mein change, obj.var badloge toh sirf us object ki naya instance var ban jaayega.
5. Instance Methods π‘
π Definition: Instance Methods are functions defined INSIDE a class that operate on individual objects. They ALWAYS take self as the first parameter β which refers to the object calling the method. Instance methods can READ and MODIFY the object's instance variables. They are the most common type of methods in a class.
π» Examples:
# Example 1: Basic instance methods class Employee: def __init__(self, name, salary): self.name = name self.salary = salary
text
# Instance method β read attribute
def display_info(self):
print(f"Name: {self.name}, Salary: βΉ{self.salary}")
# Instance method β modify attribute
def give_raise(self, amount):
self.salary += amount
print(f"β
{self.name} got βΉ{amount} raise!")
# Instance method β return value
def annual_salary(self):
return self.salary * 12
emp = Employee("Jatin", 50000)
emp.display_info() # Name: Jatin, Salary: βΉ50000
emp.give_raise(10000) # β
Jatin got βΉ10000 raise!
emp.display_info() # Name: Jatin, Salary: βΉ60000
print(f"Annual: βΉ{emp.annual_salary()}") # Annual: βΉ720000
# Example 2: Multiple methods interacting class BankAccount:
def __init__(self, holder, balance=0): self.holder = holder self.balance = balance def deposit(self, amount):
if amount <= 0:
print("β Invalid amount!") return self.balance += amount print(f"π° Deposited βΉ{amount}") def withdraw(self, amount):
if amount > self.balance:
print("β Insufficient funds!") return self.balance -= amount print(f"πΈ Withdrew βΉ{amount}") def check_balance(self):
print(f"{self.holder}'s Balance: βΉ{self.balance}") acc = BankAccount("Jatin", 5000) acc.deposit(3000) acc.withdraw(2000) acc.check_balance() # π° Deposited βΉ3000 # πΈ Withdrew βΉ2000 # Jatin's Balance: βΉ6000 acc.withdraw(10000) # β Insufficient funds!# Example 3: Method calling another method class Rectangle:
def __init__(self, length, width): self.length = length self.width = width def area(self):
return self.length * self.width def perimeter(self):
return 2 * (self.length + self.width) # Method calling other methods def display(self):
print(f"Rectangle: {self.length}x{self.width}") print(f"Area: {self.area()}") # Call another method! print(f"Perimeter: {self.perimeter()}") # Call another method! rect = Rectangle(10, 5) rect.display() # Rectangle: 10x5 # Area: 50 # Perimeter: 306. self Parameter Explained π‘
π Definition: self is a REFERENCE to the current object (instance) of a class. It is passed AUTOMATICALLY as the first parameter to every instance method β you don't pass it manually when calling. It allows methods to access and modify the object's attributes. "self" is a naming convention β you could technically use any name (like "this"), but the whole Python community uses "self".
π― Samjho Hinglish Mein: self = "yeh object khud" β matlab current object jo method call kar raha hai. Jab tum emp1.display() call karte ho, Python internally Employee.display(emp1) chalata hai β emp1 automatically self ban jaata hai. self ke through method ko pata chalta hai "mera data kya hai." self.name = "current object ka name." Bina self ke method bahar wale variables se distinguish nahi kar paayega. Interview mein 100% puchha jaata hai!
π» Examples:
# Example 1: self refers to the calling object class Employee: def __init__(self, name): self.name = name
text
def whoami(self):
print(f"I am {self.name}")
print(f"self points to: {self}")
emp1 = Employee("Jatin")
emp2 = Employee("Priya")
emp1.whoami()
# I am Jatin
# self points to:
emp2.whoami()
# I am Priya
# self points to: (different!)
# Same method, different self β that's the magic!
# Example 2: Behind the scenes β what Python does class Employee:
def __init__(self, name): self.name = name def greet(self):
print(f"Hello, {self.name}!") emp = Employee("Jatin") # These two are EQUIVALENT: emp.greet() # Normal way Employee.greet(emp) # Python does this internally! # Both output: Hello, Jatin! # Yeh proves ki self basically emp hi hai β bas Python automatically pass karta hai# Example 3: self can be renamed (but DON'T!) class Person: # Using "this" instead of "self" β VALID but NOT recommended! def __init__(this, name): this.name = name def greet(this):
print(f"Hi, {this.name}") p = Person("Jatin") p.greet() # Hi, Jatin (works but confusing!) # β οΈ ALWAYS use "self" β it's Python convention # Other developers expect "self" β using "this" will confuse them # What happens WITHOUT self? class BadClass:
def greet(): # Missing self! print("Hello") # bad = BadClass() # bad.greet() β TypeError: greet() takes 0 positional arguments but 1 was given # Python automatically passes the object β but method has no parameter to receive it!β’ Har instance method mein first parameter self hona chahiye
β’ Object create karte waqt aur method call karte waqt self manually pass NAHI karna β Python automatic karta hai
β’ self.xxx se object ke attributes access karte hain
β’ ALWAYS name it "self" β Python community standard hai
π¬ Interview Q&A:
Q: self kya hai aur kyun zaroori hai?
Ans: self ek REFERENCE hai current object (instance) ka. Har instance method ka first parameter self hota hai β jo Python AUTOMATICALLY pass karta hai jab tum method call karte ho. emp.greet() ko Python internally Employee.greet(emp) mein convert karta hai β emp automatically self ban jaata hai. self zaroori hai kyunki: (1) Method ko batata hai "kis object ka data use karna hai." (2) self.attribute se object ki instance variables access karte hain. (3) Same method different objects mein different data ke saath work karta hai self ki wajah se. Bina self ke Python nahi jaan sakta ki "current object kaunsa hai."
Q: Kya self ka naam kuch aur rakh sakte hain?
Ans: Technically HAAN β Python koi restriction nahi hai, tum this, obj, xyz kuch bhi likh sakte ho, code chal jaayega. LEKIN β Python community convention self use karti hai (PEP 8 style guide). Doosre developers tumhara code padhenge toh this dekhkar confuse honge. Django, Flask, Pandas, saari major libraries self use karti hain. Interviews mein "self" ke alawa kuch use karoge toh red flag hai. Rule: ALWAYS use "self" β kabhi naam change mat karo.
7. Class Methods & Static Methods π‘
π Definition: Besides instance methods, Python has two special types: Class Methods β take cls (class) as first parameter, decorated with @classmethod, work with class-level data. Static Methods β take NO special first parameter (no self, no cls), decorated with @staticmethod, don't access instance or class data, just utility functions grouped inside a class for organization.
π― Samjho Hinglish Mein: Teen types ke methods hain: (1) Instance method β self leta hai, object ka data use karta hai (most common). (2) Class method β cls leta hai, class-level data use karta hai (counters, factory methods). (3) Static method β na self na cls, sirf ek utility function jo class ke andar rakha hai organization ke liye. Static methods object create kiye bina bhi call ho sakte hain β MyClass.utility(). Yeh advanced OOP hai β interviews mein bahut puchha jaata hai!
π Three Types of Methods:
| Method Type | Decorator | First Param | Access To |
|---|---|---|---|
| Instance Method | None (default) | self | Instance + Class data |
| Class Method | @classmethod | cls | Class data only |
| Static Method | @staticmethod | None | Neither (independent) |
π» Examples:
# Example 1: All three method types class Employee: company = "Jatin Analytics" # Class variable total_employees = 0
text
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.total_employees += 1
# Instance method β uses self
def display(self):
print(f"Name: {self.name}, Salary: βΉ{self.salary}")
# Class method β uses cls
@classmethod
def get_total(cls):
print(f"Total Employees: {cls.total_employees}")
print(f"Company: {cls.company}")
# Static method β no self, no cls
@staticmethod
def is_workday(day):
return day.lower() not in ["saturday", "sunday"]
# Instance method β need object
emp = Employee("Jatin", 50000)
emp.display() # Name: Jatin, Salary: βΉ50000
# Class method β call via class OR object
Employee.get_total() # Total Employees: 1
emp.get_total() # Same output
# Static method β no object needed!
print(Employee.is_workday("Monday")) # True
print(Employee.is_workday("Sunday")) # False
# Example 2: Class Method as Factory (Alternative Constructor) class Employee:
def __init__(self, name, age, salary): self.name = name self.age = age self.salary = salary @classmethod def from_string(cls, emp_string): """Create Employee from CSV string""" name, age, salary = emp_string.split(",") return cls(name, int(age), int(salary)) # Return new Employee @classmethod def from_dict(cls, emp_dict): """Create Employee from dictionary""" return cls(emp_dict["name"], emp_dict["age"], emp_dict["salary"]) # Normal way emp1 = Employee("Jatin", 25, 50000) # Factory method β create from string emp2 = Employee.from_string("Priya,28,60000") # Factory method β create from dictionary emp3 = Employee.from_dict({"name": "Rahul", "age": 30, "salary": 70000}) print(emp1.name, emp1.salary) # Jatin 50000 print(emp2.name, emp2.salary) # Priya 60000 print(emp3.name, emp3.salary) # Rahul 70000# Example 3: Static methods for utility functions class MathUtils: @staticmethod def is_even(n):
return n % 2 == 0 @staticmethod def is_prime(n):
if n < 2:
return False for i in range(2, int(n ** 0.5) + 1):
if n % i == 0:
return False return True @staticmethod def factorial(n):
if n == 0:
return 1 return n * MathUtils.factorial(n - 1) # Call static methods DIRECTLY on class β no object needed! print(MathUtils.is_even(10)) # True print(MathUtils.is_prime(17)) # True print(MathUtils.factorial(5)) # 120 # Notice: MathUtils has NO __init__, no instance methods # It's just a namespace for related utility functionsβ’ Instance Method β Object-specific behavior.
emp.calculate_salary()β’ Class Method β Class-level operations, factory methods, counters.
Employee.get_total_employees(), Employee.from_csv(row)β’ Static Method β Utility functions related to class but don't need instance/class.
MathUtils.is_prime(7). Similar to regular functions but grouped in class for organization. π¬ Interview Q&A:
Q: Instance method, Class method, aur Static method mein kya difference hai?
Ans: Teen types ke methods hain: (1) Instance method β first parameter self, koi decorator nahi. Object-specific data access karta hai. (2) Class method β first parameter cls, @classmethod decorator. Class-level data access karta hai (class variables). Factory methods banane ke liye common. (3) Static method β koi first parameter nahi, @staticmethod decorator. Na instance na class ka data access karta hai β sirf utility function jo class ke andar rakha hai organization ke liye. Instance method sabse common. Class method factory pattern ke liye. Static method utilities ke liye.
Q: Static method aur regular function mein kya difference hai?
Ans: Technically kaam SAME karte hain β dono independent functions hain. Difference sirf ORGANIZATION mein hai. Static method class ke andar hoti hai β logically related utilities ko group karne ke liye. Example: MathUtils.is_prime(7) vs standalone is_prime(7). Static method: (1) Namespace organization β code cleaner. (2) IDE support better β class ke methods dikhte hain. (3) Refactoring easier β future mein instance method mein convert kar sakte ho agar zaroori ho. Regular function: standalone hoti hai β top-level. Both are equally valid β depends on code architecture and preference.
8. 4 Pillars of OOP β Overview π΄
π Definition: The 4 Pillars of OOP are the fundamental principles that define object-oriented programming: Encapsulation, Inheritance, Polymorphism, and Abstraction. These four concepts work together to make OOP powerful, flexible, and reusable. Every OOP language (Python, Java, C++) implements these pillars in some form.
π― Samjho Hinglish Mein: 4 Pillars = OOP ke 4 stambh (foundations) jinke bina OOP OOP nahi. (1) Encapsulation = data ko protect karo, private banao. (2) Inheritance = parent class se properties inherit karo (baap-beta relationship). (3) Polymorphism = "poly" = many, "morph" = forms β ek naam ke method different classes mein different kaam kare. (4) Abstraction = complex details hide karo, sirf essential dikhao. Yeh 4 concepts har OOP interview mein 100% puchhe jaate hain β memorize karo!
π The 4 Pillars β Quick Overview:
| Pillar | Meaning | Real-World Analogy | Python Implementation |
|---|---|---|---|
| Encapsulation | Bundle data + methods, hide internals | Medicine capsule β hides ingredients | _protected, __private variables |
| Inheritance | Child class inherits from parent | Child inherits parents' traits | class Child(Parent): |
| Polymorphism | Same method name, different behavior | "Speak" β dog barks, cat meows | Method overriding |
| Abstraction | Hide complex logic, show essentials | Car β steering, no engine details | Abstract classes (abc module) |
π¬ Interview Q&A:
Q: OOP ke 4 pillars kya hain?
Ans: OOP ke 4 pillars hain: (1) Encapsulation β data aur methods ko ek unit (class) mein bundle karna aur sensitive data ko protect karna (private/protected). (2) Inheritance β child class parent class ki properties aur methods inherit karti hai β code reusability aur hierarchy banati hai. (3) Polymorphism β ek hi method different classes mein different kaam kare (method overriding). (4) Abstraction β complex implementation hide karna aur sirf essential features expose karna (abstract classes). Yeh 4 concepts milke OOP ko powerful, reusable, aur maintainable banate hain. Interview mein order EIPA (Encapsulation-Inheritance-Polymorphism-Abstraction) ya real-world example ke saath explain karo.
9. Encapsulation β Public, Protected, Private π΄
π Definition: Encapsulation is the OOP principle of BUNDLING data (attributes) and methods together in a class, and RESTRICTING direct access to some of the object's components. Python uses naming conventions for access levels: Public (normal name β self.name) β accessible everywhere. Protected (single underscore β self._name) β convention says "don't access from outside," but Python doesn't enforce. Private (double underscore β self.__name) β Python does name mangling to make access harder.
π― Samjho Hinglish Mein: Encapsulation = capsule jaisa β dawai andar hai, tum baahar se cover dekhte ho. Real example: Bank account β tum balance directly modify nahi kar sakte, sirf deposit() aur withdraw() methods se access hota hai. Yeh data security hai. Python mein 3 levels: Public (name) β sab access kar sakte. Protected (_name) β "please don't touch" (convention, Python enforce nahi karta). Private (__name) β Python name mangling karta hai, access harder. Java/C++ jaisi strict private nahi hoti Python mein β "we are all adults here" philosophy.
π Access Modifiers:
| Type | Naming | Access | Enforcement |
|---|---|---|---|
| Public | name | Everywhere | No restrictions |
| Protected | _name (single _) | Class + Subclass (convention) | Only convention (not enforced) |
| Private | __name (double __) | Only inside class | Name mangling (harder access) |
π» Examples:
# Example 1: Public, Protected, Private class Employee: def __init__(self, name, salary): self.name = name # Public β accessible everywhere self._department = "IT" # Protected β convention only self.__salary = salary # Private β Python name mangling
text
def show_salary(self):
# Inside class β direct access works
print(f"{self.name}'s salary: βΉ{self.__salary}")
emp = Employee("Jatin", 50000)
# Public β free access
print(emp.name) # Jatin β
# Protected β technically works but shouldn't be used
print(emp._department) # IT β οΈ (works but bad practice)
# Private β direct access fails!
# print(emp.__salary) β AttributeError!
emp.show_salary() # Jatin's salary: βΉ50000 β
(from inside class)
# Example 2: Getter and Setter β proper way to access private class BankAccount:
def __init__(self, holder, balance): self.holder = holder self.__balance = balance # Private β cannot access directly # Getter β safe way to READ private data def get_balance(self):
return self.__balance # Setter β safe way to MODIFY with validation def deposit(self, amount):
if amount <= 0:
print("β Invalid amount!") return self.__balance += amount print(f"β
Deposited βΉ{amount}") def withdraw(self, amount):
if amount > self.__balance:
print("β Insufficient balance!") return self.__balance -= amount print(f"β
Withdrew βΉ{amount}") acc = BankAccount("Jatin", 5000) # Cannot modify directly β ENCAPSULATION protects data! # acc.__balance = 999999 β Won't affect actual __balance # MUST go through methods β validation enforced! acc.deposit(3000) # β
Deposited βΉ3000 acc.withdraw(1000) # β
Withdrew βΉ1000 acc.deposit(-500) # β Invalid amount! print(f"Balance: βΉ{acc.get_balance()}") # Balance: βΉ7000# Example 3: Name mangling β Python's private trick class Person:
def __init__(self, name): self.name = name self.__age = 25 # Private p = Person("Jatin") # Direct access fails # print(p.__age) β AttributeError! # See what Python does internally print(p.__dict__) # {'name': 'Jatin', '_Person__age': 25} # Notice: __age became _Person__age (name mangling!) # Access via mangled name (WORKS but violates encapsulation!) print(p._Person__age) # 25 (technically accessible) # Python mein "true private" nahi hai β sirf harder to access # Community trusts developers to respect the conventionobj._ClassName__var se access kar sakta hai. Python philosophy: "We are all consenting adults" β private is a HINT, not enforcement. Best practice β private variables ko access mat karo bahar se, matter of respect and clean code hai. π¬ Interview Q&A:
Q: Python mein Encapsulation kaise achieve karte hain?
Ans: Python naming conventions use karta hai access levels ke liye: (1) Public β normal name (self.name), everywhere accessible. (2) Protected β single underscore (self._name), convention says "internal use only," Python enforce nahi karta. (3) Private β double underscore (self.__name), Python name mangling karta hai β _ClassName__name ban jaata hai. Getters aur setters (or @property) use karke controlled access dete hain. Java jaisi STRICT private nahi hai Python mein β "we are all adults here" philosophy hai. Encapsulation data protection + validation + interface design ke liye zaroori hai.
Q: Name mangling kya hai?
Ans: Name mangling Python ka feature hai jo double underscore (__) waale attributes ke saath hota hai. Jab tum class Person mein self.__age likhte ho, Python internally usko _Person__age mein convert kar deta hai. Isse: (1) Direct access obj.__age se AttributeError aata hai. (2) Subclass mein accidentally same name se conflict nahi hota. (3) Truly private nahi hai β obj._Person__age se access ho sakta hai, but developer intentionally aisa nahi karta. Name mangling primarily inheritance mein name collision avoid karne ke liye designed hai β pure privacy ke liye nahi.
10. Inheritance β Single, Multiple, Multi-level π΄
π Definition: Inheritance is the OOP mechanism where a NEW class (child/subclass) inherits attributes and methods from an EXISTING class (parent/superclass). This promotes code REUSABILITY and creates a hierarchy. Syntax: class Child(Parent):. Python supports: Single Inheritance (one parent), Multi-level (chain of parents), Multiple Inheritance (multiple parents), Hierarchical (multiple children from one parent). Use super() to call parent's methods.
π― Samjho Hinglish Mein: Inheritance = baap-beta relationship. Child class parent class ki saari properties aur methods automatically inherit karti hai β bina dobara likhe. Real example: Animal parent class hai (eat, sleep methods). Dog child class extends Animal β Dog ko free mein eat aur sleep mil gaya, plus apna bark method add kar diya. Code reuse hoti hai, hierarchy bnti hai. super() se parent ke methods call kar sakte ho. Types: Single (Dog β Animal), Multi-level (Puppy β Dog β Animal), Multiple (Bat β Bird + Mammal).
π Types of Inheritance:
1. Single Inheritance: Animal β Dog (Dog inherits from Animal)
Multi-level Inheritance: Animal β Dog β Puppy
(Chain β Puppy inherits from Dog, Dog from Animal)
Multiple Inheritance: Bird ββ
ββ Bat
Mammalβ
(Bat inherits from both Bird and Mammal)
Hierarchical: Animal ββ¬β Dog
ββ Cat
ββ Fish
(Multiple children from one parent)
Syntax: class Child(Parent1, Parent2, ...):
Use super() to access parent's methods
π» Examples:
# Example 1: Single Inheritance class Animal: def __init__(self, name): self.name = name
text
def eat(self):
print(f"{self.name} is eating")
def sleep(self):
print(f"{self.name} is sleeping")
# Dog inherits from Animal
class Dog(Animal):
def bark(self):
print(f"{self.name} says Woof!")
dog = Dog("Bruno")
dog.eat() # Bruno is eating (inherited from Animal!)
dog.sleep() # Bruno is sleeping (inherited!)
dog.bark() # Bruno says Woof! (Dog's own method)
# Dog got 3 methods β 2 inherited, 1 own
# Example 2: super() β call parent's methods class Employee:
def __init__(self, name, salary): self.name = name self.salary = salary def display(self):
print(f"Name: {self.name}, Salary: βΉ{self.salary}") class Manager(Employee):
def __init__(self, name, salary, team_size): # Call parent's __init__ using super() super().__init__(name, salary) # Add manager-specific attribute self.team_size = team_size def display(self): # Call parent's display first super().display() print(f"Team Size: {self.team_size}") mgr = Manager("Jatin", 100000, 5) mgr.display() # Name: Jatin, Salary: βΉ100000 # Team Size: 5 # super() prevents code duplication!# Example 3: Multi-level & Multiple Inheritance # Multi-level: Animal β Dog β Puppy class Animal:
def breathe(self):
print("Breathing...") class Dog(Animal):
def bark(self):
print("Woof!") class Puppy(Dog): # Puppy β Dog β Animal (chain) def play(self):
print("Playing...") puppy = Puppy() puppy.breathe() # From Animal (grandparent!) puppy.bark() # From Dog (parent) puppy.play() # From Puppy itself # Multiple Inheritance class Bird:
def fly(self):
print("Flying...") class Swimmer:
def swim(self):
print("Swimming...") class Duck(Bird, Swimmer): # Multiple parents! def quack(self):
print("Quack Quack!") duck = Duck() duck.fly() # From Bird duck.swim() # From Swimmer duck.quack() # From Duck itself # Duck inherited from BOTH classes!Duck.__mro__. Simple projects mein Multiple inheritance avoid karo β complex ho jaata hai. Composition (has-a relationship) often better hai inheritance (is-a) se. π¬ Interview Q&A:
Q: Inheritance kya hai aur kitne types hain?
Ans: Inheritance = child class parent class se properties aur methods inherit karti hai β code reusability aur hierarchy provide karta hai. Syntax: class Child(Parent):. Python mein 4 types: (1) Single β one parent (Dog β Animal). (2) Multi-level β chain (Puppy β Dog β Animal). (3) Multiple β multiple parents (Duck β Bird + Swimmer). (4) Hierarchical β multiple children from one parent (Dog, Cat, Fish β Animal). super() use karke parent's methods call kar sakte hain. Multiple inheritance powerful hai but complex β Diamond problem ho sakta hai.
Q: super() kya karta hai?
Ans: super() function child class ke andar PARENT class ke methods aur __init__ ko call karne ke liye use hota hai. Common use: super().__init__(args) β child class ke __init__ mein parent's __init__ call karna. Isse code duplication avoid hoti hai β parent's initialization logic dobara likhne ki zaroorat nahi. Multiple inheritance mein super() MRO (Method Resolution Order) follow karta hai β sabse pehla parent chunta hai. Best practice: har child class ke __init__ mein pehle super().__init__() call karo, phir apne extra attributes add karo.
11. Polymorphism β Method Overriding π΄
π Definition: Polymorphism ("many forms") is the OOP principle where the SAME method name can behave DIFFERENTLY in different classes. The most common form in Python is Method Overriding β a child class provides its OWN implementation of a method that's already defined in the parent class. When called on the child object, Python uses the child's version. This allows uniform interface but different behaviors.
π― Samjho Hinglish Mein: Polymorphism = "poly" (many) + "morph" (forms) β ek naam ke method ke alag-alag roop. Real example: speak() method β Dog mein "Woof", Cat mein "Meow", Cow mein "Moo". Same method naam speak(), but har class mein different implementation. Method Overriding tab hoti hai jab child class parent ke method ko REDEFINE karti hai. Parent class ke saare children ko treat kar sakte ho uniformly β sabke saath same method call karo, sabhi apne tarike se respond karenge.
π» Examples:
# Example 1: Method Overriding β classic example class Animal: def speak(self): print("Some generic animal sound")
class Dog(Animal):
def speak(self): # Override parent's speak
print("Woof! Woof!")
class Cat(Animal):
def speak(self): # Override parent's speak
print("Meow!")
class Cow(Animal):
def speak(self):
print("Moo!")
# Polymorphism in action β SAME method, DIFFERENT behavior
animals = [Dog(), Cat(), Cow(), Animal()]
for animal in animals:
animal.speak() # Each speaks differently!
# Woof! Woof!
# Meow!
# Moo!
# Some generic animal sound
# Example 2: Extending parent's method with super() class Employee:
def __init__(self, name, salary): self.name = name self.salary = salary def show_info(self):
print(f"Name: {self.name}") print(f"Salary: βΉ{self.salary}") class Manager(Employee):
def __init__(self, name, salary, team_size): super().__init__(name, salary) self.team_size = team_size # Override + extend parent's method def show_info(self): super().show_info() # Call parent's method first print(f"Team Size: {self.team_size}") # Add extra info print(f"Role: Manager") class Developer(Employee):
def __init__(self, name, salary, language): super().__init__(name, salary) self.language = language def show_info(self): super().show_info() print(f"Language: {self.language}") print(f"Role: Developer") emp1 = Manager("Jatin", 100000, 5) emp2 = Developer("Priya", 80000, "Python") emp1.show_info() print("---") emp2.show_info() # Manager output: # Name: Jatin, Salary: βΉ100000, Team Size: 5, Role: Manager # Developer output: # Name: Priya, Salary: βΉ80000, Language: Python, Role: Developer# Example 3: Polymorphism with different classes (Duck Typing) # Even without inheritance β if class has same method, it works! class Rectangle:
def __init__(self, length, width): self.length = length self.width = width def area(self):
return self.length * self.width class Circle:
def __init__(self, radius): self.radius = radius def area(self):
return 3.14 * self.radius ** 2 class Triangle:
def __init__(self, base, height): self.base = base self.height = height def area(self):
return 0.5 * self.base * self.height # Uniform interface β all shapes have area() method shapes = [ Rectangle(10, 5), Circle(7), Triangle(6, 4) ] for shape in shapes:
print(f"{type(shape).__name__} area: {shape.area()}") # Rectangle area: 50 # Circle area: 153.86 # Triangle area: 12.0 # No inheritance needed β just same method name (Duck Typing!) # "If it walks like a duck and quacks like a duck, it's a duck"area() method hai, tum use area calculate karne ke liye use kar sakte ho β inheritance ki zaroorat nahi. Yeh Python ko flexible aur dynamic banata hai. Java/C++ mein strict interface implementation zaroori hai β Python mein nahi. π¬ Interview Q&A:
Q: Polymorphism kya hai aur kaise achieve karte hain?
Ans: Polymorphism = "many forms" β same method different classes mein different behavior. Python mein 2 tariko se achieve hoti hai: (1) Method Overriding β child class parent ke method ko REDEFINE karti hai. Same signature, different implementation. Example: Animal ka speak() method, Dog mein "Woof", Cat mein "Meow". (2) Duck Typing β different unrelated classes mein SAME method name β inheritance nahi chahiye, just consistent interface. Benefits: (1) Uniform interface across different objects. (2) Loose coupling. (3) Extensible code β new class add karna easy. Real-world: shopping cart different products handle karta hai β sab mein price() method chahiye, class alag ho sakti hai.
12. Abstraction β Abstract Classes (abc module) π΄
π Definition: Abstraction is the OOP principle of HIDING complex implementation details and exposing only ESSENTIAL features. An Abstract Class is a class that CANNOT be instantiated directly β it serves as a blueprint for other classes. It contains abstract methods (methods without implementation) that MUST be implemented by child classes. Python provides the abc module (Abstract Base Classes) with ABC class and @abstractmethod decorator.
π― Samjho Hinglish Mein: Abstraction = complex cheezein chhupao, sirf important dikhao. Real example: Car chalate ho β steering, brake, accelerator use karte ho, but engine ke andar kya ho raha hai woh tumhe nahi pata (aur nahi jaanna chahiye). Abstract class ek "contract" hai β batati hai "yeh methods honi CHAHIYE" but implementation child class deti hai. Payment abstract class me process_payment() abstract method ho β CreditCard, UPI, Wallet β sab child classes ko yeh implement karna PADEGA. Bina implement kiye instantiate nahi kar sakte.
π» Examples:
# Example 1: Abstract Class basics from abc import ABC, abstractmethod
class Animal(ABC): # Inherit from ABC = Abstract
text
@abstractmethod
def speak(self): # Abstract method β no implementation
pass
@abstractmethod
def move(self):
pass
# Regular method β can have implementation
def sleep(self):
print("Sleeping...")
# Cannot instantiate abstract class directly!
# animal = Animal() β TypeError: Can't instantiate abstract class
# Child MUST implement abstract methods
class Dog(Animal):
def speak(self):
print("Woof!")
text
def move(self):
print("Running...")
class Bird(Animal):
def speak(self):
print("Tweet!")
text
def move(self):
print("Flying...")
# Now child classes can be instantiated!
dog = Dog()
dog.speak() # Woof!
dog.move() # Running...
dog.sleep() # Sleeping... (from Animal)
bird = Bird()
bird.speak() # Tweet!
bird.move() # Flying...
# Example 2: Payment system β real-world abstraction from abc import ABC, abstractmethod class Payment(ABC):
def __init__(self, amount): self.amount = amount @abstractmethod def process_payment(self): pass def show_receipt(self):
print(f"β
Payment of βΉ{self.amount} successful") class CreditCard(Payment):
def process_payment(self):
print(f"π³ Processing βΉ{self.amount} via Credit Card...") self.show_receipt() class UPI(Payment):
def process_payment(self):
print(f"π± Processing βΉ{self.amount} via UPI...") self.show_receipt() class Wallet(Payment):
def process_payment(self):
print(f"π Processing βΉ{self.amount} via Wallet...") self.show_receipt() # Uniform interface β all payment methods work the same way payments = [ CreditCard(1000), UPI(500), Wallet(200) ] for payment in payments: payment.process_payment() print() # π³ Processing βΉ1000 via Credit Card... # β
Payment of βΉ1000 successful # π± Processing βΉ500 via UPI... # β
Payment of βΉ500 successful # π Processing βΉ200 via Wallet... # β
Payment of βΉ200 successful# Example 3: What happens if child DOESN'T implement abstract method? from abc import ABC, abstractmethod class Shape(ABC): @abstractmethod def area(self): pass @abstractmethod def perimeter(self): pass class Circle(Shape):
def __init__(self, radius): self.radius = radius def area(self):
return 3.14 * self.radius ** 2 # β οΈ FORGOT to implement perimeter()! # Try to create Circle # circle = Circle(5) # β TypeError: Can't instantiate abstract class Circle # with abstract method perimeter # Fix β implement BOTH abstract methods class Circle(Shape):
def __init__(self, radius): self.radius = radius def area(self):
return 3.14 * self.radius ** 2 def perimeter(self): # Now implemented! return 2 * 3.14 * self.radius circle = Circle(5) print(circle.area()) # 78.5 print(circle.perimeter()) # 31.4β’ Enforce a contract β child classes MUST implement specific methods
β’ Prevent instantiation of incomplete/base classes
β’ Provide common interface across related classes
β’ Framework design β Django, Flask sab abstract classes use karte hain
Real-world: Payment gateway, database drivers, plugin systems β jahan common interface chahiye but implementations different hain.
π¬ Interview Q&A:
Q: Abstract class kya hai aur Python mein kaise banate hain?
Ans: Abstract class ek class hai jo directly INSTANTIATE nahi ki ja sakti β sirf base/blueprint hoti hai other classes ke liye. Isme abstract methods hoti hain jo implementation nahi rakhtin β child classes ko implement karna PADTA hai. Python mein from abc import ABC, abstractmethod import karo. Class ko ABC se inherit karo. Method ke upar @abstractmethod decorator lagao. Agar child abstract method implement nahi karti, uski instantiation par TypeError aayega. Use cases: Payment systems, Database drivers, Plugin architectures β jahan uniform interface enforce karna hai. Frameworks like Django extensively use karti hain.
13. Dunder Methods β __str__, __repr__, __len__, __eq__ π΄
π Definition: Dunder Methods (double underscore methods) also called Magic Methods or Special Methods β are predefined methods with double underscores like __init__, __str__, __len__. Python calls them AUTOMATICALLY in specific situations. They allow you to define how your objects behave with built-in operations like print(), len(), ==, +. This makes custom classes work like built-in types.
π Common Dunder Methods:
| Method | Called By | Purpose |
|---|---|---|
| __init__ | Object creation | Constructor / initialize attributes |
| __str__ | print(obj), str(obj) | Readable string for END USER |
| __repr__ | repr(obj), obj in console | Unambiguous string for DEVELOPERS |
| __len__ | len(obj) | Return object's length |
| __eq__ | obj1 == obj2 | Define equality comparison |
| __add__ | obj1 + obj2 | Define + operator behavior |
| __lt__, __gt__ | obj1 < obj2, obj1 > obj2 | Less than, greater than |
π» Examples:
# Example 1: __str__ and __repr__ class Employee: def __init__(self, name, salary): self.name = name self.salary = salary
text
# For end users β readable
def __str__(self):
return f"{self.name} β βΉ{self.salary}"
# For developers β detailed, unambiguous
def __repr__(self):
return f"Employee(name='{self.name}', salary={self.salary})"
emp = Employee("Jatin", 50000)
# Without dunder methods:
# print(emp) β (useless!)
# With str and repr:
print(emp) # Jatin β βΉ50000 (uses str)
print(str(emp)) # Jatin β βΉ50000 (uses str)
print(repr(emp)) # Employee(name='Jatin', salary=50000) (uses repr)
# Example 2: __len__ and __eq__ class Team:
def __init__(self, name, members): self.name = name self.members = members # Enable len(team) β returns number of members def __len__(self):
return len(self.members) # Enable team1 == team2 β compare by name def __eq__(self, other):
return self.name == other.name def __str__(self):
return f"Team {self.name} with {len(self)} members" team1 = Team("Data Team", ["Jatin", "Priya", "Rahul"]) team2 = Team("Data Team", ["Amit", "Neha"]) print(len(team1)) # 3 (uses __len__) print(len(team2)) # 2 print(team1 == team2) # True (same name, uses __eq__) print(team1) # Team Data Team with 3 members# Example 3: Custom + operator (__add__) class Vector:
def __init__(self, x, y): self.x = x self.y = y def __str__(self):
return f"Vector({self.x}, {self.y})" # Enable v1 + v2 def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y) # Enable v1 - v2 def __sub__(self, other):
return Vector(self.x - other.x, self.y - other.y) # Enable v1 * scalar def __mul__(self, scalar):
return Vector(self.x * scalar, self.y * scalar) v1 = Vector(2, 3) v2 = Vector(4, 5) # Use operators like built-in types! v3 = v1 + v2 # Uses __add__ print(v3) # Vector(6, 8) v4 = v2 - v1 # Uses __sub__ print(v4) # Vector(2, 2) v5 = v1 * 3 # Uses __mul__ print(v5) # Vector(6, 9) # Custom class works like built-in β POWERFUL!β’ __str__ β end users ke liye β readable, friendly output. print() and str() call karte hain isko.
β’ __repr__ β developers ke liye β unambiguous, debugging friendly. repr() and console mein object display karta hai.
β’ Best practice: Always define
__repr__. Define __str__ only if different from __repr__.β’ If only __repr__ is defined, print() will use it as fallback.
π¬ Interview Q&A:
Q: Dunder methods kya hain aur kyun use karte hain?
Ans: Dunder methods (double underscore) β jinhe magic/special methods bhi bolte hain β predefined methods hain jo Python AUTOMATICALLY specific situations mein call karta hai. Examples: __init__ (object creation), __str__ (print), __len__ (len()), __eq__ (== comparison), __add__ (+ operator). Use karne se custom classes built-in types jaise behave karti hain β print(myobj), len(myobj), obj1 + obj2 β sab natural syntax mein. Yeh Python ka operator overloading feature hai. Real Python code mein Django, Pandas sab dunder methods extensively use karte hain β isliye samajhna zaroori hai.
Q: __str__ aur __repr__ mein kya difference hai?
Ans: Dono string representation define karte hain but different audience ke liye. __str__ β END USER ke liye β readable, friendly, informal output. print(obj) aur str(obj) isko call karte hain. __repr__ β DEVELOPER ke liye β unambiguous, formal, debugging-friendly output. Ideally aisi string return kare jise eval() se dobara object bana sako. repr(obj) aur console mein object dikhne par isko call karta hai. Best practice: Always define __repr__. __str__ optional hai β if not defined, Python falls back to __repr__. Example: Date object β __str__ mein "2026-01-15", __repr__ mein "datetime.date(2026, 1, 15)".
14. @property Decorator β Getters & Setters π΄
π Definition: The @property decorator allows you to define methods that can be accessed like attributes (without parentheses). It provides a Pythonic way to implement getters and setters β controlling how attributes are accessed, modified, or deleted. Use @property for getter, @attribute.setter for setter, and @attribute.deleter for deleter. This gives you data validation, computed properties, and encapsulation with clean syntax.
π― Samjho Hinglish Mein: Regular attribute (obj.name) directly access hoti hai β koi validation nahi. Method (obj.get_name()) parentheses ke saath call hoti hai β feel unnatural. @property beech ka rasta hai β METHOD hai internally, but ATTRIBUTE ki tarah access hoti hai (obj.name) β no parentheses! Validation, computation, encapsulation β sab possible with clean syntax. Java jaisi getName()/setName() boilerplate se bachao β Python mein @property use karo. Professional Python code mein bahut common hai.
π» Examples:
# Example 1: Basic @property β read-only attribute class Circle: def __init__(self, radius): self.radius = radius
text
@property
def area(self): # Computed property!
return 3.14 * self.radius ** 2
@property
def diameter(self):
return 2 * self.radius
c = Circle(5)
# Access like ATTRIBUTE β no parentheses!
print(c.area) # 78.5 (looks like attribute but calls method!)
print(c.diameter) # 10
# Try to set β TypeError!
# c.area = 100 β AttributeError: can't set attribute
# Read-only property banaayi hai β protects from accidental modification
# Example 2: Getter, Setter, and Deleter with validation class Employee:
def __init__(self, name, salary): self.name = name self._salary = salary # Underscore β internal # Getter @property def salary(self):
print("Reading salary...") return self._salary # Setter with validation! @salary.setter def salary(self, value):
if value < 0: raise ValueError("Salary cannot be negative!") if value > 10000000: raise ValueError("Salary too high! Suspicious!") print(f"Setting salary to βΉ{value}") self._salary = value # Deleter @salary.deleter def salary(self):
print("Deleting salary...") del self._salary emp = Employee("Jatin", 50000) # Get β calls getter print(emp.salary) # Reading salary... 50000 # Set β calls setter with validation emp.salary = 60000 # Setting salary to βΉ60000 print(emp.salary) # Reading salary... 60000 # Validation prevents invalid values # emp.salary = -1000 β ValueError: Salary cannot be negative! # Delete β calls deleter del emp.salary # Deleting salary...# Example 3: Real-world β Temperature converter class Temperature:
def __init__(self, celsius): self._celsius = celsius @property def celsius(self):
return self._celsius @celsius.setter def celsius(self, value):
if value < -273.15: raise ValueError("Below absolute zero!") self._celsius = value # Computed property β Fahrenheit derived from Celsius @property def fahrenheit(self):
return (self._celsius * 9/5) + 32 @fahrenheit.setter def fahrenheit(self, value): self._celsius = (value - 32) * 5/9 temp = Temperature(25) print(f"Celsius: {temp.celsius}Β°C") # 25Β°C print(f"Fahrenheit: {temp.fahrenheit}Β°F") # 77Β°F # Set fahrenheit β automatically updates celsius! temp.fahrenheit = 100 print(f"Celsius: {temp.celsius}Β°C") # ~37.78Β°C print(f"Fahrenheit: {temp.fahrenheit}Β°F") # 100Β°F # Validation works # temp.celsius = -300 β ValueError: Below absolute zero!β’ Clean syntax β
obj.name instead of obj.get_name()β’ Encapsulation β control access without ugly getter/setter calls
β’ Validation β validate values before assignment
β’ Computed properties β derived from other attributes
β’ Backwards compatible β turn attribute into property later without breaking code
β’ Read-only attributes β define only @property (no setter) β immutable
π¬ Interview Q&A:
Q: @property decorator kya karta hai?
Ans: @property Python ka decorator hai jo method ko attribute ki tarah access karne ki permission deta hai β no parentheses needed. Yeh Pythonic way of implementing getters and setters hai. Benefits: (1) Clean syntax β obj.area instead of obj.get_area(). (2) Validation β setter mein input validate kar sakte ho. (3) Computed properties β dynamically calculated attributes (like area from radius). (4) Read-only β only getter define karo, no setter = immutable attribute. Syntax: @property for getter, @attr.setter for setter, @attr.deleter for deleter. Java-style get_xxx()/set_xxx() methods se bahut cleaner hai. Modern Python best practice hai β professional code mein bahut use hota hai.
Quick Revision β OOP Complete Summary
| # | Topic | Key Takeaway |
|---|---|---|
| 1 | OOP Introduction | Organize code around objects (data + behavior). Everything in Python is object. |
| 2 | Class & Object | Class = blueprint, Object = instance. One class β many objects. |
| 3 | Constructor (__init__) | Auto-called on object creation. Initializes attributes. First param: self. |
| 4 | Instance vs Class Vars | Instance = per object (self.x). Class = shared by all (ClassName.x). |
| 5 | Instance Methods | Functions inside class. First param self. Access/modify instance data. |
| 6 | self Parameter | Reference to current object. Passed automatically. NEVER rename. |
| 7 | Class & Static Methods | @classmethod (cls), @staticmethod (no param). Factory methods and utilities. |
| 8 | 4 Pillars of OOP | Encapsulation, Inheritance, Polymorphism, Abstraction |
| 9 | Encapsulation | public, _protected, __private. Name mangling. Data protection. |
| 10 | Inheritance | class Child(Parent). Single, Multi-level, Multiple. super() calls parent. |
| 11 | Polymorphism | Same method, different behavior. Method overriding. Duck typing. |
| 12 | Abstraction | Hide details, show essentials. ABC + @abstractmethod. Enforce contract. |
| 13 | Dunder Methods | __str__, __repr__, __len__, __eq__, __add__. Operator overloading. |
| 14 | @property | Pythonic getters/setters. Validation + clean syntax. Read-only attributes. |
π Python Handbook β Complete Series Summary
Python Handbook ka complete journey β Basics se lekar OOP tak β sab kuch cover ho gaya:
| Part | Topic | Key Concepts |
|---|---|---|
| Part 1 | Python Basics | Variables, Data Types, Operators, Input/Output, Strings |
| Part 2 | Strings & Numbers | String methods, math module, random module, formatting |
| Part 3 | Lists | Creation, methods, comprehension, nested lists, time complexity |
| Part 4 | Tuples | Immutable sequences, packing/unpacking, named tuples |
| Part 5 | Sets | Unique elements, set operations (union, intersection) |
| Part 6 | Dictionaries | Key-value pairs, methods, nested dicts, comprehension |
| Part 7 | Control Flow | if/elif/else, for/while loops, break/continue/pass |
| Part 8 | Functions & Lambda | def, args/kwargs, scope, lambda, decorators, type hints |
| Part 9 | File Handling & Exceptions | open/read/write, try/except, custom exceptions |
| Part 10 | OOP | Class, Object, 4 Pillars, Dunder Methods, @property |
π Python Handbook β COMPLETE!
Python ki complete journey β Variables se lekar OOP tak, Strings se lekar Decorators tak β sab kuch cover ho gaya. Ab tum Python ka master ho β Data Analysis, Web Development, Machine Learning, Automation β kahin bhi apply kar sakte ho. Previously completed on Data Insights: MySQL Masterclass (7 Parts), Pandas, NumPy (3 Parts), Data Cleaning + Statistics, Matplotlib, Seaborn, Plotly, Excel Masterclass (8 Parts), Power BI Masterclass (7 Parts), SQL Interview Questions (Basic + Intermediate + Advanced), Case Studies, Career Roadmap β aur ab Python Handbook (10 Parts) COMPLETE!
Happy Learning & Keep Coding! π
β Data Insights by Jatin Analytics
π¬ Comments (0)
Loading comments...