Lesson 4
Navigating Single Inheritance in Python: A Comprehensive Guide for Beginners
Introduction to the Lesson

Hello, future Python expert! Today, we'll dig into a significant feature of Object-Oriented Programming (OOP) — inheritance. Much like children inheriting characteristics from their parents, child classes in OOP can adopt properties and behaviors from parent classes in the same vein.

Our goal here is to comprehend the purpose, different types, and the implementation of inheritance, with speculative attention on simple inheritance. With inheritance, we can reuse code, minimizing repetition and maximizing efficiency.

Understanding Inheritance: The What and Why

Inheritance in OOP mirrors inherited characteristics in life. We create child classes that receive properties and behaviors from a parent class. Consider a school environment: all kinds of teachers share common actions (such as teaching), regardless of the subjects they teach. We could create a base Teacher class with these shared behaviors and then generate MathTeacher, ScienceTeacher, etc., which inherit these common behaviors and add others unique to their respective subjects.

Python supports multiple types of inheritance — including single, multiple, multilevel, and more. In this lesson, we'll turn our attention to single inheritance, where a child class inherits from a solitary parent class. This type of inheritance is the most common and basic among all existing.

Diagram:

Markdown
1 BaseClass 2 | 3 DerivedClass
Implementing Simple Inheritance

Let's examine how to implement inheritance in Python. We begin by creating a Teacher base class:

Python
1class Teacher: 2 def __init__(self, name): 3 self.name = name 4 5 def teach(self): 6 print(self.name + " is teaching.")

Next, a MathTeacher child class inherits from Teacher:

Python
1class MathTeacher(Teacher): 2 def teach_math(self): 3 print(self.name + " is teaching math.")

The MathTeacher class inherits the Teacher class because in the class constructor, we have class MathTeacher(Teacher). The MathTeacher inherits the teach method from Teacher and incorporates a teach_math method:

Python
1math_teacher = MathTeacher("Mr. Johnson") 2math_teacher.teach() # Prints: "Mr. Johnson is teaching." 3math_teacher.teach_math() # Prints: "Mr. Johnson is teaching math."

In the above code, math_teacher can invoke both teach and teach_math methods because teach was inherited from the Teacher class.

Use of Inheritance: Code Reuse and Method Overriding

Inheritance enables code reuse; this is evident when the MathTeacher class utilizes the teach method from the Teacher class without the necessity to re-implement it. Moreover, Python allows method overriding. If a base-class method does not suit the child's class, it can be overridden — for example, a math teacher doesn't just 'teach'; they 'explain':

Python
1class MathTeacher(Teacher): 2 def teach(self): 3 print(self.name + " is explaining math.") 4 5math_teacher = MathTeacher("Mr. Johnson") 6math_teacher.teach() # Prints: "Mr. Johnson is explaining math."

The method in the MathTeacher class will supersede the one in the Teacher class. Note how we called the same teach() method on the MathTeacher instance but observed a different behavior - that's because the parent method from the Teacher class has been overridden.

Utilizing super() in Constructors for Inheritance

So far, we've seen how a child class inherits properties and behaviors from a parent class. But what if we want to initialize some attributes in the parent and others in the child class? That's where super() comes in.

The super() function is a built-in function in Python that allows us to refer to the parent class, typically used in the constructor of the child class. It lets us call methods from the parent class without naming the parent class explicitly, making our code more maintainable.

Let's look at an example. Suppose we have a Person class with attributes name and age, and a Student class that extends Person and has an additional attribute student_id:

Python
1class Person: 2 def __init__(self, name, age): 3 self.name = name 4 self.age = age 5 6class Student(Person): 7 def __init__(self, name, age, student_id): 8 super().__init__(name, age) # call the constructor of Person 9 self.student_id = student_id 10 11student = Student('Alex', 20, 'S1023') 12print(student.name) # Outputs: Alex 13print(student.age) # Outputs: 20 14print(student.student_id) # Outputs: S1023

In this code, Student's constructor calls super().__init__(name, age) to use Person's constructor to set the name and age attributes. Then it adds a student_id attribute.

In summary, the super() function provides a way to call parent class methods, helping to avoid code duplication and enhancing code maintainability.

Lesson Summary and Next Steps

Well done! You're now familiar with Python inheritance. We've learned its definition, why it's imperative, and implemented simple inheritance. To solidify your new skills, try applying these concepts with upcoming exercises.

In the forthcoming lessons, we'll be delving into the other potent components of OOP in Python — encapsulation and polymorphism. So, stay tuned for an exciting journey into the cosmos of object-oriented programming!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.