๐Ÿ“ Inheritance in Python ๐Ÿš—๐Ÿ“š๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ

๐Ÿ“ Inheritance in Python ๐Ÿš—๐Ÿ“š๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ#

Inheritance is a core concept of object-oriented programming (OOP) that allows a class (the child class) to reuse, extend, or modify the behavior of another class (the parent class). This helps you avoid code repetition and maintain a clear, logical structure in your software.

Why Use Inheritance?#

  1. Reusability

  • Child classes automatically inherit attributes and methods from the parent class.

  • Avoids duplicating code shared by multiple classes.

  1. Extensibility

  • Children can add or modify functionality while preserving shared behavior from the parent.

  1. Organizational Clarity

  • Logical hierarchy mirrors real-world or conceptual relationships (e.g., Car is a type of Vehicle).

Basic Example of Single Inheritance#

class Vehicle:
    def __init__(self, make, model):
        self.make = make
        self.model = model

    def start_engine(self):
        print(f"{self.make} {self.model} engine started!")


class Car(Vehicle):
    def __init__(self, make, model, doors):
        # Call parent constructor using super()
        super().__init__(make, model)
        self.doors = doors

    def open_trunk(self):
        print(f"{self.make} {self.model} trunk opened.")


# Creating instances
my_car = Car("Toyota", "Corolla", 4)
my_car.start_engine()  # Inherited from Vehicle
my_car.open_trunk()  # Defined in Car
Toyota Corolla engine started!
Toyota Corolla trunk opened.

Key Points

- `Car` inherits all attributes and methods from `Vehicle` (like `start_engine`).
- `super().init()` calls the parent class constructor.
- `Car` adds new functionality: `open_trunk`.

Overriding Methods#

If the child class defines a method with the same name as a method in the parent class, it overrides that method:

class Vehicle:
    def start_engine(self):
        print("Vehicle's engine started (generic).")


class Car(Vehicle):
    def start_engine(self):
        print("Car's engine started (car-specific).")


my_car = Car()
my_car.start_engine()  # "Car's engine started (car-specific)."
Car's engine started (car-specific).

Using super() in an Overridden Method#

Sometimes you still want to call the original parent method, then add extra steps:

class Car(Vehicle):
    def start_engine(self):
        super().start_engine()
        print("Performing additional car checks...")


my_car = Car()

my_car.start_engine()
Vehicle's engine started (generic).
Performing additional car checks...

Practical Examples#

  1. Shape Hierarchy (Geometry)

class Shape:
    def area(self):
        raise NotImplementedError("Subclasses must implement this method.")


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

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


my_circle = Circle(5)
print(my_circle.area())  # 78.53975
78.53975
  • Circle overrides area() from Shape.

  • Enforces a pattern where every shape must implement its own area().

  1. User Permissions (Web/App)

class User:
    def __init__(self, username):
        self.username = username

    def can_access(self, resource):
        # Basic users might have minimal access
        return resource in ["public"]


class Admin(User):
    def can_access(self, resource):
        # Admins can access everything
        return True


user = User("john_doe")
admin = Admin("admin_user")
print(user.can_access("public"))  # True
print(user.can_access("private"))  # False
print(admin.can_access("private"))  # True (overridden behavior)
True
False
True

Composition vs. Inheritance#

  • Composition: Instead of inheriting, you have one class hold an instance of another.

class Engine:
    def start(self):
        print("Engine started.")


class Vehicle:
    def __init__(self):
        self.engine = Engine()

    def start_engine(self):
        self.engine.start()
  • When something is not strictly a specialized form of a parent class, composition may be more natural.

  • When to Use Inheritance

  1. IS-A Relationship: Car is a Vehicle.

  1. Shared or Extended Behavior: Common logic in the parent, specialized in the child.

6. Best Practices#

  1. Use super()

    • Ensures correct initialization of parent class attributes.

    • Helps maintain readability in child methods.

  1. Keep Hierarchies Simple

    • Deep, complex inheritance chains can be difficult to understand and maintain.

  1. Override Responsibly

    • Only override methods if you need to provide different or enhanced behavior.

  1. Enforce Consistency

    • If your parent class has certain expectations (e.g., a move() method), ensure child classes follow the same interface.