Python Tutorial › Object-Oriented Python › Polymorphism & Encapsulation

Python Polymorphism & Encapsulation

IntermediateLesson 33 of 4711 min read6 runnable examples

Polymorphism means “many forms”: different objects respond to the same method call in their own way. Encapsulation means keeping an object’s data behind its methods so it cannot be put into an invalid state. Together with inheritance they are the core ideas of object-oriented programming.

Advertisement
In this lesson
  • Polymorphism with overriding and duck typing
  • Operator overloading with special methods
  • Private attributes and name mangling
  • Getters and setters with @property

Polymorphism through overriding

Each class implements the same method name. Code that calls the method does not need to know which class it is dealing with.

Python
class Shape:    def area(self):        raise NotImplementedError class Circle(Shape):    def __init__(self, r):        self.r = r    def area(self):        return round(3.14159 * self.r ** 2, 2) class Square(Shape):    def __init__(self, side):        self.side = side    def area(self):        return self.side ** 2 for shape in [Circle(1), Square(3), Circle(2)]:    print(type(shape).__name__, shape.area())
Output
Circle 3.14
Square 9
Circle 12.57

Duck typing

Python does not require a shared parent class. If an object has the method you call, it works. “If it walks like a duck and quacks like a duck, it is a duck.”

Python
class PdfReport:    def export(self):        return "report.pdf" class CsvReport:    def export(self):        return "report.csv" def save_all(reports):    for r in reports:        print("saved", r.export()) save_all([PdfReport(), CsvReport()])
Output
saved report.pdf
saved report.csv

Built-in functions are polymorphic too: len() works on strings, lists and dicts because each type implements __len__.

Python
for thing in ["abc", [1, 2], {"a": 1}, range(10)]:    print(type(thing).__name__, len(thing))
Output
str 3
list 2
dict 1
range 10

Operator overloading

Special methods let your own classes work with +, ==, < and friends:

Python
class Money:    def __init__(self, amount):        self.amount = amount     def __add__(self, other):        return Money(self.amount + other.amount)     def __lt__(self, other):        return self.amount < other.amount     def __repr__(self):        return f"Money({self.amount})" a, b = Money(250), Money(100)print(a + b)print(a < b, sorted([a, b]))
Output
Money(350)
False [Money(100), Money(250)]
OperatorSpecial method
a + b__add__
a - b__sub__
a * b__mul__
a == b__eq__
a < b__lt__
len(a)__len__
str(a)__str__

Encapsulation: protecting data

Python has no true private attributes, but it has two conventions:

  • _name (one underscore): “internal, please do not touch from outside”.
  • __name (two underscores): Python renames it to _ClassName__name (name mangling), so it is hard to reach by accident.
Python
class Wallet:    def __init__(self):        self._currency = "INR"      # internal by convention        self.__balance = 0          # name-mangled     def add(self, amount):        self.__balance += amount     def balance(self):        return self.__balance w = Wallet()w.add(500)print(w.balance(), w._currency)print(w._Wallet__balance)          # still reachable if you insistprint(w.__balance)
Output
500 INR
500
Traceback (most recent call last):
  File "main.py", line 16, in <module>
    print(w.__balance)
          ^^^^^^^^^^^
AttributeError: 'Wallet' object has no attribute '__balance'. Did you mean '.balance' instead of '.__balance'?

Getters and setters with @property

@property lets you validate values while keeping simple attribute syntax for the caller:

Python
class Product:    def __init__(self, name, price):        self.name = name        self.price = price          # goes through the setter below     @property    def price(self):        return self._price     @price.setter    def price(self, value):        if value < 0:            raise ValueError("price cannot be negative")        self._price = value     @property    def price_with_tax(self):       # read-only computed attribute        return round(self._price * 1.18, 2) p = Product("Mouse", 349)p.price = 399print(p.price, p.price_with_tax) try:    p.price = -5except ValueError as e:    print("Error:", e)
Output
399 470.82
Error: price cannot be negative
Tip

Start with plain public attributes. Switch to @property only when you need validation or a computed value; callers do not need to change their code.

Check your understanding

Predict the output

class Cat:    def speak(self): return "meow"class Robot:    def speak(self): return "beep" print([x.speak() for x in (Cat(), Robot())])

Quick quiz

1. What does duck typing mean?

2. What does __add__ enable?

3. What happens to self.__secret inside class Box?

Exercise: temperature with validation

Create a Thermostat with a target property that only accepts values from 16 to 30 and raises ValueError otherwise.

Input
set target to 24, then try 35
Expected output
24
Rejected: 35 is outside 16-30
24
Hint 1

Store the real value in self._target inside the setter.

Program Practice

48 Python OOP Programs

Every program has a step-by-step explanation, colour-coded code and its real output.

Start practising →
Related sets:All practice sets

Polymorphism and encapsulation FAQ

What is polymorphism in Python with an example?

It is the ability of different objects to respond to the same call. len("abc") and len([1, 2]) both work, and shape.area() returns the right result for a Circle or a Square.

Does Python have private variables?

Not strictly. A leading underscore marks an attribute as internal, and a double underscore triggers name mangling. Both are conventions that discourage outside access.

What is @property in Python?

A decorator that turns a method into an attribute-like getter. Add @name.setter to run validation when the attribute is assigned.

Does Python support method overloading?

Not by signature. Define one method with default or variable arguments (*args) instead. Operator overloading is supported through special methods such as __add__.

Leave a Comment