Absolutely πŸ‘ Let’s understand OOPs in Python in the easiest possible way, with simple real-life examples.

 

🐍 OOPs in Python

OOP = Object-Oriented Programming

Think of OOP as creating a blueprint and then creating real things from that blueprint.

Real-life example

Suppose Car is a blueprint.

A car has:

  • Properties: color, brand, speed
  • Actions: start(), stop(), drive()

In Python:

class Car:
    def __init__(self, color, brand):
        self.color = color
        self.brand = brand

    def drive(self):
        print("Car is driving")


car1 = Car("Red", "BMW")

print(car1.color)
print(car1.brand)

car1.drive()

Output:

Red
BMW
Car is driving

1. Class

A class is a blueprint/template.

class Student:
    pass

Here, Student is a class.

Think:

Class = Blueprint


2. Object

An object is a real instance of a class.

class Student:
    pass

student1 = Student()
student2 = Student()

Here:

  • Student → Class
  • student1 → Object
  • student2 → Object

Think:

Class = House blueprint
Object = Actual house


3. __init__() Constructor

__init__() runs automatically when we create an object.

class Student:

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


student1 = Student("Rahul", 20)

print(student1.name)
print(student1.age)

Output:

Rahul
20

What is self?

self means the current object.

self.name = name

Means:

Store name inside this particular object.


4. Encapsulation

Encapsulation = keeping data and methods together inside a class.

Example:

class BankAccount:

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

    def deposit(self, amount):
        self.balance += amount


account = BankAccount(1000)

account.deposit(500)

print(account.balance)

Output:

1500

The balance and the functions that work on it are kept inside BankAccount.


5. Inheritance

Inheritance = child class gets properties/methods from parent class.

Real-life example:

Animal → Dog

Dog is an Animal.

class Animal:

    def eat(self):
        print("Animal is eating")


class Dog(Animal):

    def bark(self):
        print("Dog is barking")


dog = Dog()

dog.eat()
dog.bark()

Output:

Animal is eating
Dog is barking

Dog inherited eat() from Animal.

Think:

Parent → Child


6. Polymorphism

Polymorphism = same method name, different behavior.

Example:

class Dog:

    def sound(self):
        print("Dog says Woof")


class Cat:

    def sound(self):
        print("Cat says Meow")


dog = Dog()
cat = Cat()

dog.sound()
cat.sound()

Output:

Dog says Woof
Cat says Meow

Same method:

sound()

Different behavior.

Think:

One name → Many forms


7. Abstraction

Abstraction = hiding unnecessary details and showing only what is needed.

For example, when you drive a car:

You use:

Start
Accelerator
Brake
Steering

You don't need to know exactly how the engine works internally.

Python example:

from abc import ABC, abstractmethod


class Animal(ABC):

    @abstractmethod
    def sound(self):
        pass


class Dog(Animal):

    def sound(self):
        print("Woof")


dog = Dog()

dog.sound()

Output:

Woof

⭐ Four Main Pillars of OOP

Remember these four:

OOP ConceptEasy Meaning
EncapsulationKeep data + methods together
InheritanceChild gets features from parent
PolymorphismSame method, different behavior
AbstractionHide unnecessary implementation

🧠 Interview shortcut

E → I → P → A

Encapsulation → Inheritance → Polymorphism → Abstraction

And remember:

Class = Blueprint
Object = Real thing
__init__ = Constructor
self = Current object



Sure πŸ‘ Here is a simple English explanation of the four pillars of OOP in Python, with easy examples.

OOPs: Four Main Pillars

Remember:

E → I → P → A

  • E — Encapsulation
  • I — Inheritance
  • P — Polymorphism
  • A — Abstraction

1. Encapsulation

Meaning

Encapsulation means keeping data and the methods that work on that data together inside a class.

It also helps protect data from being directly changed from outside.

Example

class BankAccount:

    def __init__(self, balance):
        self.__balance = balance

    def deposit(self, amount):
        self.__balance += amount

    def get_balance(self):
        return self.__balance


account = BankAccount(1000)

account.deposit(500)

print(account.get_balance())

Output:

1500

Here:

  • __balance is private.
  • We don't directly access it.
  • We use deposit() and get_balance() to work with it.

Easy to remember:

Encapsulation = Data hiding + Data protection


2. Inheritance

Meaning

Inheritance means a child class can use the properties and methods of a parent class.

Example

class Animal:

    def eat(self):
        print("Animal is eating")


class Dog(Animal):

    def bark(self):
        print("Dog is barking")


dog = Dog()

dog.eat()
dog.bark()

Output:

Animal is eating
Dog is barking

Dog gets the eat() method from Animal.

Easy diagram

Animal
   ↓
  Dog

Easy to remember:

Inheritance = Reuse code from parent class


Types of Inheritance

There are 5 common types in Python.

1. Single Inheritance

One parent → One child

class Animal:
    def eat(self):
        print("Eating")


class Dog(Animal):
    def bark(self):
        print("Barking")


dog = Dog()
dog.eat()
dog.bark()
Animal
  ↓
 Dog

2. Multiple Inheritance

One child → Multiple parents

class Father:
    def skills(self):
        print("Driving")


class Mother:
    def talent(self):
        print("Cooking")


class Child(Father, Mother):
    pass


child = Child()

child.skills()
child.talent()
Father ──┐
         ↓
       Child
         ↑
Mother ──┘

3. Multilevel Inheritance

Grandparent → Parent → Child

class Grandfather:
    def house(self):
        print("Has a house")


class Father(Grandfather):
    def car(self):
        print("Has a car")


class Son(Father):
    def bike(self):
        print("Has a bike")


son = Son()

son.house()
son.car()
son.bike()
Grandfather
     ↓
   Father
     ↓
    Son

4. Hierarchical Inheritance

One parent → Multiple children

class Animal:

    def eat(self):
        print("Eating")


class Dog(Animal):
    def bark(self):
        print("Barking")


class Cat(Animal):
    def meow(self):
        print("Meowing")


dog = Dog()
cat = Cat()

dog.eat()
cat.eat()
       Animal
       /    \
     Dog    Cat

5. Hybrid Inheritance

Combination of two or more types of inheritance.

class A:
    def show_a(self):
        print("A")


class B(A):
    def show_b(self):
        print("B")


class C(A):
    def show_c(self):
        print("C")


class D(B, C):
    def show_d(self):
        print("D")


obj = D()

obj.show_a()
obj.show_b()
obj.show_c()
obj.show_d()
       A
      / \
     B   C
      \ /
       D

3. Polymorphism

Meaning

Polymorphism means "one name, many forms."

The same method can behave differently depending on the object.

Example

class Dog:

    def sound(self):
        print("Woof")


class Cat:

    def sound(self):
        print("Meow")


dog = Dog()
cat = Cat()

dog.sound()
cat.sound()

Output:

Woof
Meow

Both classes have:

sound()

But they behave differently.

Easy to remember:

Polymorphism = Same method, different behavior


Types/Forms of Polymorphism in Python

1. Method Overriding

Child class changes the behavior of a parent method.

class Animal:

    def sound(self):
        print("Animal makes sound")


class Dog(Animal):

    def sound(self):
        print("Dog says Woof")


dog = Dog()

dog.sound()

Output:

Dog says Woof

The child Dog overrides the parent sound().


2. Duck Typing

Python focuses on what an object can do, rather than its exact type.

class Dog:

    def sound(self):
        print("Woof")


class Cat:

    def sound(self):
        print("Meow")


def make_sound(animal):
    animal.sound()


make_sound(Dog())
make_sound(Cat())

Output:

Woof
Meow

The function doesn't care whether it receives a Dog or Cat.

It only cares that the object has a sound() method.


4. Abstraction

Meaning

Abstraction means hiding implementation details and showing only the important functionality.

Real-life example:

When you use an ATM:

Insert Card
     ↓
Enter PIN
     ↓
Withdraw Money

You don't need to know how the bank's internal system processes the transaction.

Python Example

from abc import ABC, abstractmethod


class Animal(ABC):

    @abstractmethod
    def sound(self):
        pass


class Dog(Animal):

    def sound(self):
        print("Woof")


dog = Dog()

dog.sound()

Output:

Woof

Animal says:

Every animal must have a sound() method.

But it doesn't specify exactly how the sound should be produced.

Dog provides the actual implementation.

Easy to remember:

Abstraction = Hide implementation + Show necessary functionality


⭐ Final Interview Revision

ConceptSimple MeaningExample
EncapsulationProtect/hide dataBank Account
InheritanceReuse parent codeAnimal → Dog
PolymorphismSame method, different behaviorDog/Cat → sound()
AbstractionHide implementation detailsATM / Abstract Class

🧠 One-line trick

EncapsulationProtect data
InheritanceReuse code
PolymorphismDifferent behavior
AbstractionHide details

If you're preparing for a Python interview, these four plus class, object, self, __init__, method overriding, and super() are the core OOP topics to know.


Yes πŸ‘ In OOP, Polymorphism is commonly explained in two forms:

  1. Compile-time Polymorphism
  2. Run-time Polymorphism

But there is an important point for Python: Python is dynamically typed and does not have traditional compile-time method overloading like Java/C++. Python mainly achieves polymorphism at runtime.


1. Compile-Time Polymorphism

Meaning

The method or operation to use is determined during compilation.

A common example is Method Overloading.

Example in Java/C++

add(int, int)
add(int, int, int)

The compiler decides which add() method should be called based on the number/type of arguments.

What about Python?

Python does not support traditional method overloading.

For example, this does not work as expected:

class Calculator:

    def add(self, a, b):
        return a + b

    def add(self, a, b, c):
        return a + b + c

The second add() replaces the first one.

Instead, Python can achieve similar behavior using default arguments:

class Calculator:

    def add(self, a, b, c=0):
        return a + b + c


calculator = Calculator()

print(calculator.add(10, 20))
print(calculator.add(10, 20, 30))

Output:

30
60

So, in a Python interview, say:

Python does not support traditional compile-time method overloading. We can achieve similar behavior using default arguments, *args, etc.


2. Run-Time Polymorphism

Meaning

The method that will execute is determined at runtime, depending on the object.

The most common example is Method Overriding.

class Animal:

    def sound(self):
        print("Animal makes a sound")


class Dog(Animal):

    def sound(self):
        print("Dog says Woof")


class Cat(Animal):

    def sound(self):
        print("Cat says Meow")


animals = [Dog(), Cat()]

for animal in animals:
    animal.sound()

Output:

Dog says Woof
Cat says Meow

Here:

animal.sound()

is the same method call, but Python decides at runtime which version to execute.

Easy diagram

             Animal
            sound()
           /       \
          /         \
       Dog           Cat
     sound()       sound()
       ↓              ↓
     Woof            Meow

This is Run-Time Polymorphism → Method Overriding.


⭐ Compile-Time vs Run-Time

FeatureCompile-TimeRun-Time
Decision madeDuring compilationDuring execution
Common exampleMethod OverloadingMethod Overriding
Python support❌ No traditional overloading✅ Yes
Exampleadd(a,b) / add(a,b,c)Dog.sound() / Cat.sound()

🧠 Interview Answer

If interviewer asks "What are the types of polymorphism?", you can say:

There are two common types: compile-time polymorphism and run-time polymorphism. Compile-time polymorphism is usually achieved through method overloading, while run-time polymorphism is achieved through method overriding. Python does not support traditional compile-time method overloading, but it supports run-time polymorphism very well through method overriding and duck typing.

No. You cannot define multiple __init__() methods in the same Python class and have all of them work.

If you write multiple __init__() methods, the last one replaces the previous one.

❌ Example

class Car:

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

    def __init__(self, color, brand):
        self.color = color
        self.brand = brand


car = Car("Red", "BMW")

Python uses only the second __init__().

The first one is overwritten.


✅ How to handle different numbers of arguments

Use default arguments:

class Car:

    def __init__(self, color, brand=None):
        self.color = color
        self.brand = brand


car1 = Car("Red")
car2 = Car("Blue", "BMW")

print(car1.color)
print(car2.color, car2.brand)

Output:

Red
Blue BMW

So you can create objects in different ways without creating multiple __init__() methods.

impportant points : 

Great question πŸ‘ self is one of the most important concepts in Python OOP.

What is self?

self refers to the current object.

In simple words:

self tells Python which object's data or method you are talking about.

Example

class Car:
    def __init__(self, color, brand):
        self.color = color
        self.brand = brand

    def drive(self):
        print(self.brand, "is driving")


car1 = Car("Red", "BMW")
car2 = Car("Blue", "Audi")

print(car1.color)
print(car2.color)

car1.drive()
car2.drive()

Output:

Red
Blue
BMW is driving
Audi is driving

Why do we need self?

Look at this:

self.color = color

There are two different colors:

color       → value received by __init__()
self.color  → value stored inside the object

When we do:

car1 = Car("Red", "BMW")

Python creates:

car1
 ├── color = "Red"
 └── brand = "BMW"

When we do:

car2 = Car("Blue", "Audi")

Python creates another object:

car2
 ├── color = "Blue"
 └── brand = "Audi"

self makes this possible.


Think of self as "my"

Imagine car1 says:

My color is Red.

Python represents "my" using self:

self.color = color

For car1:

self → car1

For car2:

self → car2

So:

car1.drive()

means approximately:

drive(car1)

and:

car2.drive()

means approximately:

drive(car2)

Why self in every method?

class Car:

    def drive(self):
        print(self.brand)

    def stop(self):
        print(self.brand, "stopped")

self allows the method to access the specific object's attributes.

Without self, Python wouldn't know which object's brand you mean.

🧠 Remember this

self = current object

car1 → self
car2 → self
car3 → self

The same class can create thousands of objects, and self tells Python which object is currently being used.

 

Comments

Popular posts from this blog

⭐ UNIT – 3 (Easy Notes + PDF References) Wireless LAN • MAC Problems • Hidden/Exposed Terminal • Near/Far • Infrastructure vs Ad-hoc • IEEE 802.11 • Mobile IP • Ad-hoc Routing

UNIT-I: Innovation – Basic Definition and Classification (MIE)

UNIT–5 (Simplified & Easy Notes) Software Architecture Documentation