Inheritance in Python: Understanding Superclasses and Subclasses

Inheritance in Python: Understanding Superclasses and Subclasses

Inheritance in Python: Understanding Superclasses and Subclasses
Inheritance in Python: Understanding Superclasses and Subclasses

Inheritance is a powerful feature in object-oriented programming that allows a subclass to inherit attributes and methods from a superclass. Inheritance allows for code reuse and modularity, and it is a key concept in many programming languages, including Python.

In this article, we will introduce the basics of inheritance and explore how to use it in Python. We will cover topics such as defining superclasses and subclasses, method overriding, method resolution order (MRO), polymorphism, and abstract base classes (ABCs).

Introduction to inheritance

Inheritance is a mechanism that allows a new class to be created based on an existing class. The new class is called the “subclass,” and the existing class is the “superclass.” The subclass can have additional attributes and methods, but it can also use all the attributes and methods of the superclass. This allows for code reuse and modularity.

There are several types of inheritance:

  • Single inheritance: A subclass inherits from a single superclass.
  • Multiple inheritance: A subclass inherits from multiple superclasses.
  • Multilevel inheritance: A subclass inherits from a superclass, which itself inherits from another superclass.
  • Hierarchical inheritance: Multiple subclasses inherit from a single superclass.

Inheritance is a useful tool for creating relationships between classes, such as a “is-a” relationship (e.g., a car is a vehicle). However, it is important to use inheritance wisely and avoid creating overly complex class hierarchies.

Defining a superclass and subclass in Python

In Python, you can define a class using the class keyword, followed by the class name and a colon. The class body is indented, and you can define attributes and methods within the class. Here is an example of a simple superclass called Shape:

class Shape:
    def __init__(self, sides):
        self.sides = sides
    
    def area(self):
        pass  # method to be implemented in subclass

To create a subclass, you can use the same class keyword and specify the superclass in parentheses. The subclass will automatically inherit all the attributes and methods of the superclass. Here is an example of a subclass called Rectangle that inherits from the Shape superclass:

class Rectangle(Shape):
    def __init__(self, width, height):
        super().__init__(4)  # call the superclass __init__ method
        self.width = width
        self.height = height
    
    def area(self):
        return self.width * self.height

In the Rectangle subclass, we use the super() function to call the __init__ method of the Shape superclass. This allows us to set the sides attribute of the Shape class, as well as the width and height attributes of the Rectangle class.

See also  Create simple Calorie Calculator app using tkinter with code and explanation

Overriding methods

Sometimes, you may want to define a subclass method that has the same name as a superclass method, but with different behavior. This is called “method overriding.” To override a method in a subclass, you can simply define a method with the same name in the subclass. The subclass method will take precedence over the superclass method. Here is an example of method overriding in Python:

class Shape:
    def __init__(self, sides):
        self.sides = sides
    
    def area(self):
        pass  # method to be implemented in subclass

class Rectangle(Shape):
    def __init__(self, width, height):
        super().__init__(4)
        self.width = width
        self.height = height
    
    def area(self):  # override the area method
        return self.width * self.height

class Triangle(Shape):
    def __init__(self, base, height):
        super().__init__(3)
        self.base = base
        self.height = height
    
    def area(self):  # override the area method
        return 0.5 * self.base * self.height

In this example, both the Rectangle and Triangle classes override the area method of the Shape superclass. The Rectangle class calculates the area using the width and height attributes, while the Triangle class calculates the area using the base and height attributes.

If you want to call the superclass method from within the subclass method, you can use the super() function. For example:

class Shape:
    def __init__(self, sides):
        self.sides = sides
    
    def area(self):
        return 0

class Rectangle(Shape):
    def __init__(self, width, height):
        super().__init__(4)
        self.width = width
        self.height = height
    
    def area(self):  # override the area method
        return super().area() + self.width * self.height  # call the superclass area method

In this example, the Rectangle class calls the area method of the Shape superclass using the super() function, and then adds the area calculated from the width and height attributes.

Method resolution order (MRO)

In Python, the order in which superclasses are searched for method resolution is called the “method resolution order” (MRO). The MRO is determined by the class hierarchy and the order in which superclasses are listed in parentheses. You can view the MRO of a class using the __mro__ attribute. For example:

print(Rectangle.__mro__)  # (Rectangle, Shape, object)
print(Triangle.__mro__)  # (Triangle, Shape, object)

You can also use the mro() method of a class to view the MRO. For example:

print(Rectangle.mro())  # [Rectangle, Shape, object]
print(Triangle.mro())  # [Triangle, Shape, object]

It is important to understand the MRO when working with inheritance, as it can affect the behavior of method resolution.

See also  Step-by-Step Guide to Creating a Simple Calculator using Tkinter in Python

Polymorphism and method overloading

Polymorphism is the ability for a subclass to override or extend the methods of the superclass. In Python, you can use polymorphism by defining methods with the same name in different classes, and then using them in a generic way.

Method overloading is a form of polymorphism where you define multiple methods with the same name, but with different arguments. Python does not support method overloading directly, but you can achieve a similar effect by using default arguments. Here is an example of polymorphism and method overloading in Python:

class Shape:
    def __init__(self, sides):
        self.sides = sides
    
    def area(self):
        pass  # method to be implemented in subclass

class Rectangle(Shape):
    def __init__(self, width, height):
        super().__init__(4)
        self.width = width
        self.height = height
    
    def area(self):  # override the area method
        return self.width * self.height

class Triangle(Shape):
    def __init__(self, base, height):
        super().__init__(3)
        self.base = base
        self.height = height
    
    def area(self):  # override the area method
        return 0.5 * self.base * self.height

def get_area(shape, *args):
    return shape.area(*args)

rect = Rectangle(10, 5)
print(get_area(rect))  # 50

tri = Triangle(10, 5)
print(get_area(tri))  # 25

In this example, the get_area function is a generic function that accepts a Shape object and calculates the area using the area method of the object. The Rectangle and Triangle classes both override the area method with their own implementation, so the get_area function can be used with either a Rectangle or Triangle object. We can also achieve a similar effect to method overloading by using default arguments in the area method. For example:

class Shape:
    def __init__(self, sides):
        self.sides = sides
    
    def area(self, base=None, height=None):
        if base is not None and height is not None:
            return 0.5 * base * height
        else:
            pass  # method to be implemented in subclass

class Rectangle(Shape):
    def __init__(self, width, height):
        super().__init__(4)
        self.width = width
        self.height = height
    
    def area(self):  # override the area method
        return super().area(self.width, self.height)

class Triangle(Shape):
    def __init__(self, base, height):
        super().__init__(3)
        self.base = base
        self.height = height
    
    def area(self):  # override the area method
        return super().area(self.base, self.height)

rect = Rectangle(10, 5)
print(rect.area())  # 50

tri = Triangle(10, 5)
print(tri.area())  # 25

In this example, the area method of the Shape class has default arguments for base and height. The Rectangle and Triangle classes override the area method and call the superclass method with their own base and height values. This allows for a similar effect to method overloading, where the area method can be called with different arguments depending on the subclass.

See also  Creating a Simple Image Viewer App using Tkinter - A Step-by-Step Guide with Explanation

Abstract base classes (ABCs)

Abstract base classes (ABCs) are classes that define methods that must be implemented by their concrete subclasses. ABCs are useful for defining interfaces or contracts that must be followed by concrete classes. In Python, you can use the ABC module and the abstractmethod decorator to define ABCs. Here is an example:

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    def area(self):  # implement the abstract area method
        return self.width * self.height

class Triangle(Shape):
    def __init__(self, base, height):
        self.base = base
        self.height = height
    
    def area(self):  # implement the abstract area method
        return 0.5 * self.base * self.height

rect = Rectangle(10, 5)
print(rect.area())  # 50

tri = Triangle(10, 5)
print(tri.area())  # 25

# Cannot create an instance of the abstract Shape class
# shape = Shape()  # TypeError: Can't instantiate abstract class Shape with abstract methods area

In this example, the Shape class is defined as an ABC using the ABC module and the abstractmethod decorator. The area method is defined as an abstract method, which means it must be implemented by any concrete subclasses of the Shape class.

The Rectangle and Triangle classes are concrete subclasses of the Shape class, so they must implement the area method. They do this by defining the area method and providing their own implementation. You cannot create an instance of an ABC, only concrete subclasses. If you try to instantiate an ABC, you will get a TypeError stating that you cannot instantiate an abstract class.

Conclusion

Inheritance is a powerful tool in object-oriented programming that allows for code reuse and modularity. It is based on the concepts of encapsulation, inheritance, and polymorphism, which allow for the creation of relationships between classes and the ability to override or extend methods. In Python, you can define superclasses and subclasses using the class keyword and the super() function, and you can use the __mro__ attribute and the mro() method to view the method resolution order. You can also use polymorphism and default arguments to achieve a similar effect to method overloading, and you can use ABCs and the ABC and abstractmethod modules to define interfaces or contracts for concrete classes.

Print Friendly, PDF & Email

Author

Leave a Reply