INHERITENCE

Inheritance is a fundamental concept in object-oriented programming (OOP) that allows a class (subclass) to inherit attributes and methods from another class (superclass). This promotes code reuse and facilitates the creation of a hierarchy of classes with shared functionality. In Python, inheritance is implemented using the class definition syntax.

Syntax:

class Superclass:
    # Superclass attributes and methods

class Subclass(Superclass):
    # Subclass attributes and methods, may override superclass methods

Types of Inheritance:

  1. Single Inheritance: In single inheritance, a subclass inherits from only one superclass.
    class Animal:
        def speak(self):
            print("Animal speaks")
    
    class Dog(Animal):
        pass
    
    dog = Dog()
    dog.speak()  # Output: Animal speaks
    
  2. Multiple Inheritance: In multiple inheritance, a subclass inherits from multiple superclasses. Python supports multiple inheritance, allowing a class to inherit from more than one superclass.
    class Animal:
        def speak(self):
            print("Animal speaks")
    
    class Mammal:
        def walk(self):
            print("Mammal walks")
    
    class Dog(Animal, Mammal):
        pass
    
    dog = Dog()
    dog.speak()  # Output: Animal speaks
    dog.walk()   # Output: Mammal walks
    
  3. Multilevel Inheritance: In multilevel inheritance, a subclass inherits from another subclass. This creates a chain of inheritance.
    class Animal:
        def speak(self):
            print("Animal speaks")
    
    class Dog(Animal):
        def bark(self):
            print("Dog barks")
    
    class Puppy(Dog):
        pass
    
    puppy = Puppy()
    puppy.speak()  # Output: Animal speaks
    puppy.bark()   # Output: Dog barks
    
  4. Hierarchical Inheritance: In hierarchical inheritance, multiple subclasses inherit from a single superclass.
    class Animal:
        def speak(self):
            print("Animal speaks")
    
    class Dog(Animal):
        pass
    
    class Cat(Animal):
        pass
    
    dog = Dog()
    cat = Cat()
    dog.speak()  # Output: Animal speaks
    cat.speak()  # Output: Animal speaks
    
  5. Hybrid (or Complex) Inheritance: Hybrid inheritance is a combination of multiple inheritance and other types of inheritance.
    class Animal:
        def speak(self):
            print("Animal speaks")
    
    class Mammal(Animal):
        def walk(self):
            print("Mammal walks")
    
    class Flying:
        def fly(self):
            print("Flying")
    
    class Bat(Mammal, Flying):
        pass
    
    bat = Bat()
    bat.speak()  # Output: Animal speaks
    bat.walk()   # Output: Mammal walks
    bat.fly()    # Output: Flying
    

Inheritance is a powerful mechanism in Python that enables code reuse and promotes modular design. However, it should be used judiciously to maintain code readability and minimize complexity.


In Python, properties and decorators are advanced features used to control attribute access and modify the behavior of functions. They are commonly used in object-oriented programming to ensure encapsulation and provide additional functionality to classes and methods.

Properties:

Properties allow you to define special methods (getter, setter, deleter) for accessing and modifying attributes of a class. They provide a way to implement computed attributes or to add validation and error checking.

Syntax:

class MyClass:
    def __init__(self):
        self._x = None

    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, value):
        # Optional: Add validation logic
        self._x = value

    @x.deleter
    def x(self):
        # Optional: Add cleanup logic
        del self._x

Usage:

obj = MyClass()
obj.x = 10  # Calls the setter method
print(obj.x)  # Calls the getter method
del obj.x  # Calls the deleter method

Decorators:

Decorators are functions that modify the behavior of other functions or methods. They allow you to wrap a function with another function to add functionality before or after the original function executes.

Syntax:

def decorator(func):
    def wrapper(*args, **kwargs):
        # Add functionality before calling the original function
        result = func(*args, **kwargs)
        # Add functionality after calling the original function
        return result
    return wrapper

@decorator
def my_function():
    pass

Usage:

my_function()  # Calls my_function and applies the decorator

Example:

class MyClass:
    def __init__(self):
        self._x = None

    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, value):
        if value < 0:
            raise ValueError("Value must be non-negative")
        self._x = value

obj = MyClass()
obj.x = 10
print(obj.x)  # Output: 10

obj.x = -5  # Raises ValueError

Properties and decorators are powerful features in Python that enhance code readability, maintainability, and reusability. They provide a clean and concise way to implement advanced functionality in classes and functions.