Intro to Python Programming

🐍Intro to Python Programming Unit 13 – Inheritance

Inheritance is a core concept in object-oriented programming that allows new classes to be based on existing ones. It promotes code reuse and modularity by enabling derived classes to inherit attributes and methods from base classes, establishing hierarchical relationships and supporting polymorphism. Python implements inheritance using the class keyword, with derived classes inheriting from base classes. Various types of inheritance exist, including single, multilevel, hierarchical, multiple, and hybrid. Method overriding allows child classes to customize inherited behavior, while composition offers an alternative approach to building complex objects.

What is Inheritance?

  • Inheritance is a fundamental concept in object-oriented programming (OOP) that allows a new class to be based on an existing class
  • Enables the derived class, also known as the child class or subclass, to inherit attributes and methods from the base class, also known as the parent class or superclass
  • Promotes code reuse and modularity by allowing common functionality to be defined in a base class and shared among multiple derived classes
  • Establishes an "is-a" relationship between the derived class and the base class, meaning that an instance of the derived class can be treated as an instance of the base class
  • Supports the creation of hierarchical class structures, where more specialized classes are derived from more general base classes
  • Facilitates polymorphism, allowing objects of different derived classes to be treated as objects of the base class and respond to the same method calls in different ways
  • Enhances code maintainability and extensibility by centralizing common code in base classes and allowing modifications to be made in a single place

Types of Inheritance

  • Single Inheritance
    • Involves a single base class and a single derived class
    • The derived class inherits attributes and methods from a single parent class
    • Represents a simple one-to-one relationship between classes
  • Multilevel Inheritance
    • Involves a chain of inheritance where a derived class serves as the base class for another derived class
    • The inheritance hierarchy can extend to multiple levels, with each class inheriting from its immediate parent class
    • Allows for the creation of specialized classes based on more general classes
  • Hierarchical Inheritance
    • Involves multiple derived classes inheriting from a single base class
    • Each derived class independently inherits attributes and methods from the common parent class
    • Enables the creation of diverse specialized classes that share a common base
  • Multiple Inheritance
    • Allows a derived class to inherit from multiple base classes simultaneously
    • The derived class combines attributes and methods from all its parent classes
    • Can lead to complex class hierarchies and potential naming conflicts, requiring careful design and resolution mechanisms
  • Hybrid Inheritance
    • Combines multiple types of inheritance, such as single, multilevel, and multiple inheritance
    • Results in complex class hierarchies that incorporate various inheritance relationships
    • Requires careful planning and consideration to ensure proper functionality and avoid ambiguity

Syntax and Basic Implementation

  • In Python, inheritance is implemented using the
    class
    keyword followed by the name of the derived class and the base class in parentheses
  • The derived class can inherit attributes and methods from the base class using the
    super()
    function or by directly accessing the base class
  • Example syntax:
    class BaseClass:
        # Base class definition
        pass
    
    class DerivedClass(BaseClass):
        # Derived class definition
        pass
    
  • The derived class can add its own attributes and methods in addition to the inherited ones
  • The
    __init__()
    method in the derived class can call the
    __init__()
    method of the base class using
    super().__init__()
    to initialize inherited attributes
  • Example implementation:
    class Animal:
        def __init__(self, name):
            self.name = name
    
        def speak(self):
            pass
    
    class Dog(Animal):
        def speak(self):
            return "Woof!"
    
    dog = Dog("Buddy")
    print(dog.name)  # Output: Buddy
    print(dog.speak())  # Output: Woof!
    

Parent and Child Classes

  • In inheritance, the base class is referred to as the parent class or superclass, while the derived class is referred to as the child class or subclass
  • The parent class defines common attributes and methods that are inherited by the child class
  • The child class can extend or specialize the functionality of the parent class by adding new attributes and methods or overriding existing ones
  • The child class inherits all public and protected members (attributes and methods) of the parent class
    • Public members are accessible from anywhere, including outside the class
    • Protected members (denoted by a single underscore prefix) are intended to be accessed within the class and its subclasses
  • The child class does not inherit private members (denoted by a double underscore prefix) of the parent class directly
    • Private members are name-mangled to avoid naming conflicts and are not intended to be accessed outside the class
  • The child class can access the parent class's attributes and methods using the
    super()
    function or by directly referencing the parent class
  • Example:
    class Vehicle:
        def __init__(self, brand):
            self.brand = brand
    
        def start(self):
            print("Vehicle started.")
    
    class Car(Vehicle):
        def __init__(self, brand, model):
            super().__init__(brand)
            self.model = model
    
        def drive(self):
            print("Car is driving.")
    
    car = Car("Toyota", "Camry")
    print(car.brand)  # Output: Toyota
    car.start()  # Output: Vehicle started.
    car.drive()  # Output: Car is driving.
    

Method Overriding

  • Method overriding occurs when a child class defines a method with the same name as a method in its parent class
  • The overridden method in the child class provides a different implementation or extends the functionality of the parent class's method
  • When an overridden method is called on an instance of the child class, the child class's implementation is executed instead of the parent class's implementation
  • Method overriding allows the child class to customize or specialize the behavior inherited from the parent class
  • To override a method, the child class defines a method with the same name and signature as the method in the parent class
  • The
    super()
    function can be used to call the parent class's implementation of the overridden method if needed
  • Example:
    class Shape:
        def area(self):
            pass
    
    class Rectangle(Shape):
        def __init__(self, width, height):
            self.width = width
            self.height = height
    
        def area(self):
            return self.width * self.height
    
    class Circle(Shape):
        def __init__(self, radius):
            self.radius = radius
    
        def area(self):
            return 3.14 * self.radius ** 2
    
    shapes = [Rectangle(4, 5), Circle(3)]
    for shape in shapes:
        print(shape.area())  # Output: 20 \n 28.26
    

Multiple Inheritance

  • Multiple inheritance allows a class to inherit from multiple parent classes simultaneously
  • The child class inherits attributes and methods from all its parent classes
  • Multiple inheritance is achieved by specifying multiple parent classes in the class definition, separated by commas
  • Example syntax:
    class ParentClass1:
        # Parent class 1 definition
        pass
    
    class ParentClass2:
        # Parent class 2 definition
        pass
    
    class ChildClass(ParentClass1, ParentClass2):
        # Child class definition
        pass
    
  • When a method is called on an instance of the child class, Python follows a method resolution order (MRO) to determine which parent class's implementation to use
    • The MRO is based on the C3 linearization algorithm and ensures a consistent and predictable order of method resolution
  • Multiple inheritance can lead to naming conflicts if multiple parent classes define methods or attributes with the same name
    • To resolve naming conflicts, the child class can explicitly specify which parent class's implementation to use by referencing the parent class directly
  • Example:
    class Flyer:
        def fly(self):
            print("Flying...")
    
    class Swimmer:
        def swim(self):
            print("Swimming...")
    
    class Duck(Flyer, Swimmer):
        pass
    
    duck = Duck()
    duck.fly()  # Output: Flying...
    duck.swim()  # Output: Swimming...
    

Inheritance vs. Composition

  • Inheritance and composition are two fundamental concepts in object-oriented programming for establishing relationships between classes
  • Inheritance represents an "is-a" relationship, where a child class inherits attributes and methods from a parent class
    • It focuses on creating specialized classes based on more general classes
    • Inheritance promotes code reuse and allows for the creation of hierarchical class structures
    • However, inheritance can lead to tight coupling between classes and may result in complex class hierarchies
  • Composition represents a "has-a" relationship, where a class contains instances of other classes as its attributes
    • It focuses on creating complex objects by combining simpler objects or components
    • Composition allows for more flexible and loosely coupled designs, as the composed objects can be easily replaced or modified
    • Composition promotes encapsulation and modularity, as each class has a specific responsibility and can be developed and tested independently
  • The choice between inheritance and composition depends on the specific requirements and design goals of the system
    • Inheritance is suitable when there is a clear hierarchical relationship between classes and when the child class is a specialized version of the parent class
    • Composition is preferred when the relationship between classes is not hierarchical and when flexibility and modularity are important
  • In practice, a combination of inheritance and composition can be used to create effective and maintainable object-oriented designs
  • Example:
    class Engine:
        def start(self):
            print("Engine started.")
    
    class Wheel:
        def rotate(self):
            print("Wheel rotating.")
    
    class Car:
        def __init__(self):
            self.engine = Engine()
            self.wheels = [Wheel() for _ in range(4)]
    
        def start(self):
            self.engine.start()
            for wheel in self.wheels:
                wheel.rotate()
    
    car = Car()
    car.start()  # Output: Engine started. \n Wheel rotating. \n Wheel rotating. \n Wheel rotating. \n Wheel rotating.
    

Practical Applications

  • Inheritance is widely used in various domains to model real-world relationships and create reusable and modular code
  • GUI frameworks (Graphical User Interface)
    • Inheritance is used to create specialized GUI components based on base classes provided by the framework
    • For example, a custom button class can inherit from a generic button class and add specific functionality or appearance
  • Game development
    • Inheritance is used to create hierarchies of game objects, such as characters, enemies, and items
    • Common attributes and behaviors can be defined in base classes, while specialized classes can inherit and extend them
  • Database ORM (Object-Relational Mapping)
    • Inheritance is used to map class hierarchies to database tables
    • ORM frameworks often provide base classes for defining database models, which can be inherited to create specific entity classes
  • Web frameworks
    • Inheritance is used to create reusable and modular components in web development frameworks
    • For example, a custom view class can inherit from a base view class provided by the framework and override specific methods or add additional functionality
  • Scientific computing and simulations
    • Inheritance is used to model complex systems and entities in scientific simulations
    • Base classes can define common properties and behaviors, while specialized classes can inherit and extend them to represent specific entities or phenomena
  • Machine learning and data analysis
    • Inheritance is used to create hierarchies of algorithms, models, and data processing pipelines
    • Common functionality can be defined in base classes, while specific implementations can inherit and specialize them
  • Embedded systems and device drivers
    • Inheritance is used to create modular and reusable device drivers and firmware components
    • Base classes can define common interfaces and protocols, while specialized classes can inherit and implement specific device functionalities


© 2024 Fiveable Inc. All rights reserved.
AP® and SAT® are trademarks registered by the College Board, which is not affiliated with, and does not endorse this website.

© 2024 Fiveable Inc. All rights reserved.
AP® and SAT® are trademarks registered by the College Board, which is not affiliated with, and does not endorse this website.
Glossary
Glossary