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

Encapsulation and Inheritance

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

Encapsulation and inheritance are two of the four core pillars of object-oriented programming. Encapsulation controls who can access an object's data, while inheritance lets one class reuse and extend the behavior of another — together they are the foundation for writing organized, maintainable class hierarchies.

Encapsulation: Bundling and Protecting Data

Encapsulation means bundling data and the methods that operate on it inside a class, while restricting direct, uncontrolled access to that data from outside the class.

Public, Protected, and Private Attributes

ConventionSyntaxMeaning
Publicself.nameFreely accessible from anywhere — the default in Python
Protectedself._nameA convention signaling "internal use only" — not enforced by Python, just a hint to other developers
Privateself.__nameName-mangled by Python to discourage (but not fully prevent) outside access
class Account:
    def __init__(self, balance):
        self.__balance = balance   # "private" — double underscore prefix

    def get_balance(self):
        return self.__balance

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
        else:
            print("Deposit amount must be positive")

acc = Account(1000)
print(acc.get_balance())   # 1000
# print(acc.__balance)     # AttributeError — name has been mangled internally
Unlike Java's private keyword, Python does not truly enforce privacy — double-underscore attributes are "name-mangled" to _ClassName__attribute internally, which can technically still be accessed if you know the mangled name. Python relies on developer discipline and convention far more than strict access control.

Getters and Setters

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

    def get_balance(self):
        return self.__balance

    def set_balance(self, amount):
        if amount >= 0:
            self.__balance = amount
        else:
            print("Balance cannot be negative")

acc = Account(500)
acc.set_balance(-100)   # rejected, prints the warning
acc.set_balance(800)    # accepted
print(acc.get_balance())   # 800

Inheritance: Reusing and Extending a Class

Inheritance lets a new class (the child or subclass) acquire the attributes and methods of an existing class (the parent or superclass), establishing an "is-a" relationship.

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

    def eat(self):
        print(f"{self.name} is eating")

class Dog(Animal):   # Dog inherits from Animal
    def bark(self):
        print(f"{self.name} says Woof!")

my_dog = Dog("Rex")
my_dog.eat()    # Rex is eating  — inherited from Animal
my_dog.bark()   # Rex says Woof! — defined directly in Dog

The super() Function

super() lets a child class call a method from its parent class explicitly — most commonly used inside a child's own __init__ to reuse the parent's setup logic.

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

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)   # let Animal handle the name attribute
        self.breed = breed

my_dog = Dog("Rex", "Labrador")
print(my_dog.name)    # Rex
print(my_dog.breed)   # Labrador

Method Overriding

A child class can redefine a method that already exists in the parent class, replacing the parent's behavior with its own.

class Animal:
    def speak(self):
        print("Some generic animal sound")

class Cat(Animal):
    def speak(self):   # overrides Animal's version
        print("Meow")

Animal().speak()   # Some generic animal sound
Cat().speak()       # Meow
Interview tip: "Does Python support multiple inheritance?" — Yes, unlike Java. A class can inherit from more than one parent: class Child(Parent1, Parent2). Python resolves potential conflicts using the Method Resolution Order (MRO), which interviewers sometimes ask about by name for stronger candidates.