🐍
Syllabus / Python Programming / Phase 4: OOP Concepts, File Handling, Exception Handling (Days 16–20)
Intermediate

Polymorphism and Abstraction

📂 Phase 4: OOP Concepts, File Handling, Exception Handling (Days 16–20) · Python Programming

Polymorphism and abstraction round out the four pillars of object-oriented programming. Polymorphism lets the same method name behave differently depending on the object calling it; abstraction hides implementation complexity behind a clean, consistent interface.

Polymorphism: Same Method Name, Different Behavior

class Dog:
    def speak(self):
        print("Woof!")

class Cat:
    def speak(self):
        print("Meow!")

class Cow:
    def speak(self):
        print("Moo!")

animals = [Dog(), Cat(), Cow()]
for animal in animals:
    animal.speak()   # each object responds to speak() in its OWN way
# Woof!
# Meow!
# Moo!

This is polymorphism in action — the calling code (animal.speak()) doesn't need to know or care exactly which class each object belongs to; it just trusts that every object responds to speak() appropriately.

Polymorphism With a Common Parent Class

class Animal:
    def speak(self):
        raise NotImplementedError("Subclass must implement this method")

class Dog(Animal):
    def speak(self):
        print("Woof!")

class Cat(Animal):
    def speak(self):
        print("Meow!")

for animal in [Dog(), Cat()]:
    animal.speak()

Python's Built-In Polymorphism

Polymorphism isn't limited to your own classes — Python's built-in operators and functions are polymorphic too. The + operator, for example, behaves completely differently depending on the operand types.

print(2 + 3)            # 5        — numeric addition
print("Py" + "thon")    # Python   — string concatenation
print([1, 2] + [3, 4])  # [1, 2, 3, 4] — list concatenation

Abstraction: Hiding Implementation Details

Abstraction means exposing only the essential operations an object supports, while hiding exactly how those operations are implemented internally. In Python, abstraction is most formally achieved with the abc (Abstract Base Class) module.

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass   # no implementation here — subclasses MUST provide one

class Rectangle(Shape):
    def __init__(self, length, width):
        self.length = length
        self.width = width

    def area(self):
        return self.length * self.width

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14159 * self.radius ** 2

shapes = [Rectangle(4, 5), Circle(3)]
for shape in shapes:
    print(shape.area())
# 20
# 28.27431

Why Abstract Classes Matter

# shape = Shape()   # TypeError — cannot instantiate an abstract class directly
rect = Rectangle(4, 5)   # works fine — Rectangle provides a concrete area()
An abstract class cannot be instantiated on its own — it exists purely to define a contract that every subclass is forced to fulfill. If a subclass fails to implement an abstract method, Python raises a TypeError the moment you try to create an instance of it.

Encapsulation, Inheritance, Polymorphism, Abstraction — At a Glance

PillarCore Idea
EncapsulationBundle data + methods; restrict direct access to internal state
InheritanceReuse and extend behavior from a parent class
PolymorphismSame method name, different behavior depending on the object
AbstractionExpose only essential operations; hide implementation details
Interview tip: "What is the difference between abstraction and encapsulation?" trips up many candidates — encapsulation is about HIDING DATA via access control (private attributes, getters/setters), while abstraction is about HIDING IMPLEMENTATION COMPLEXITY by exposing only a simple, essential interface. They work together but solve genuinely different problems.