Concepts / Polymorphism and Method Resolution

Polymorphism and Method Resolution

A base class defines shared attributes and methods; derived classes inherit from it and add their own specialized features.

  • Programming

One Interface, Different Results

Suppose a program stores both teachers and students as school members. The program can ask every member to tell() about themselves without first checking whether the member is a teacher or a student. The same method call can produce teacher-specific or student-specific behavior because each derived class provides its own version of tell(). That combination of shared structure and specialized behavior is the central idea behind inheritance, method resolution, and polymorphism.

object is Teacherobject is Studentrunsrunsmember.tell()same callTeacherTeacher.tell()Teacher detailsname, age, salaryStudentStudent.tell()Student detailsname, age, marks
How can the same method call produce different behavior for objects from different derived classes?

Base and Derived Classes

A base class, also called a superclass, contains attributes and methods shared by a group of related objects. A derived class, also called a subclass, inherits those attributes and methods and can add specialized features of its own. Inheritance lets you define common functionality once instead of repeating the same attributes and methods in every related class.

inheritsinheritsSchoolMembername, age, tell()Teachersalary, tell()Studentmarks, tell()
Which functionality belongs in the shared base class, and which functionality is specialized in each subclass?

Separating Shared and Specialized Data

Model teachers and students without defining name and age separately in both classes.

Create the base class: SchoolMember holds the attributes and method common to all school members: name, age, and tell().

Create the teacher subclass: Teacher inherits name, age, and tell() from SchoolMember, then adds salary and teacher-specific behavior.

Create the student subclass: Student inherits name, age, and tell() from SchoolMember, then adds marks and student-specific behavior.

Use the inherited features: A Teacher object and a Student object both have name and age because those attributes come from SchoolMember.

The base class contains shared functionality, while each derived class adds only the features that distinguish it.

Initialization Order

When a derived class defines its own __init__ method, it should call the parent class __init__ method to initialize the inherited attributes. In the school example, creating a Teacher first enters Teacher.__init__. That method explicitly calls SchoolMember.__init__(self, name, age), which initializes name and age. After the parent initialization completes, Teacher.__init__ adds salary.

callsexplicitly callsreturns tofinishesTeacher(...)object creationTeacher.__init__startsSchoolMember.__init__creates name, agesalarycreated by TeacherTeacher objectname, age, salary
How does initialization flow from a subclass constructor to the parent constructor, and which attributes are created at each step?

What do you think happens?

When a Teacher object is created, which initialization message appears first: the SchoolMember message or the Teacher message?

  • SchoolMember first, then Teacher
  • Teacher first, then SchoolMember
  • Only the Teacher message
  • Only the SchoolMember message
Reveal answer

Answer: SchoolMember first, then Teacher

Teacher.__init__ explicitly calls SchoolMember.__init__ before it completes the Teacher-specific initialization. The base initialization therefore runs before the derived class finishes.

Overriding an Inherited Method

Method overriding occurs when a derived class defines a method with the same name as a method in its base class. The derived version replaces the inherited version for objects of that derived class. A subclass can still reuse the parent's implementation by calling ParentClassName.method_name(self). In the school example, Teacher.tell() and Student.tell() each call SchoolMember.tell(self) first and then add specialized information.

object is Teacherobject is Studentresolves toresolves tomay callmay callobject.tell()method callTeacher objectsubclass method existsTeacher.tell()runs firstSchoolMember.tell()called explicitly whenneededStudent objectsubclass method existsStudent.tell()runs first
When the same method is defined in both a base class and a subclass, which implementation runs for a subclass object?

When overriding a method, call the parent version when the shared behavior is still needed. This allows the derived method to extend the inherited behavior instead of repeating the base method's implementation.

Polymorphic Method Calls

Polymorphism allows code to use the same method call with objects from different derived classes. A loop over Teacher and Student objects can call member.tell() for every object without checking its specific class. Python resolves the call to Teacher.tell() for a teacher and Student.tell() for a student. The caller uses one shared method name, while each object supplies its appropriate behavior.

each object receivesTeacher.tell()Student.tell()membersTeacher and Student objectsmember.tell()same call in loopTeacher behaviorname, age, salaryStudent behaviorname, age, marks
How does one loop use a shared method call while receiving different results from different subclasses?

In the school model, the loop does not need separate branches such as “if this is a Teacher” and “if this is a Student.” It simply calls tell() on each school member. Adding another derived class with its own tell() implementation would allow the same style of loop to work with that class as well.

Shared Functionality

definesdefinesdefinesreused byreused byinherited or overridden byinherited or overridden bySchoolMembershared definitionnameinherited attributeTeacheradds salaryageinherited attributeStudentadds markstell()inherited method
Which attributes and methods are defined once in the base class and reused by every derived class?
ClassShared featuresSpecialized featuretell() behavior
SchoolMembername and ageNone describedDisplays shared member details
Teachername and agesalaryDisplays shared details and salary
Studentname and agemarksDisplays shared details and marks

Mistakes with Inheritance

  • Skipping the parent __init__ call

    The inherited name and age attributes are not initialized by the base class.

    Fix: Call the parent initializer before adding the derived class's attributes.

  • Assuming the base tell() method always runs

    The derived method overrides the inherited method for that derived object.

    Fix: Call SchoolMember.tell(self) inside the overriding method when the shared behavior is needed.

  • Duplicating shared attributes in every subclass

    This repeats functionality that belongs in the common base class and makes maintenance harder.

    Fix: Define shared attributes and methods once in SchoolMember and inherit them.

  • Checking every concrete type before calling tell()

    The code gives up the flexibility provided by polymorphism.

    Fix: Use the shared method call and let Python resolve the appropriate derived implementation.

Practice the Resolution Path

MEDIUM

A list contains one Teacher object and one Student object. For each object, trace what happens when a loop calls member.tell(). Identify which tell() implementation runs, which shared details are displayed, and which specialized detail is added.

Hints
  • Both objects are school members, so both have name and age.
  • Look for the tell() method defined on the object's own derived class first.
  • Teacher adds salary, while Student adds marks.

Tracing Two Objects

Determine the behavior of a loop that calls member.tell() for a Teacher followed by a Student.

First iteration: The current object is a Teacher, so the call resolves to Teacher.tell().

Reuse shared behavior: Teacher.tell() calls SchoolMember.tell(self), displaying the teacher's name and age.

Add teacher behavior: Teacher.tell() then displays the teacher's salary.

Second iteration: The current object is a Student, so the call resolves to Student.tell().

Add student behavior: Student.tell() uses the shared school-member behavior and then displays the student's marks.

The loop uses one call, member.tell(), but the selected implementation and specialized output depend on the current object's derived class.

Key Takeaways

  1. A base class defines functionality shared by related objects, while derived classes inherit that functionality and add specialized features. A derived initializer should call the parent __init__ method so inherited attributes are initialized. A derived class can override a method to replace or extend inherited behavior, using ParentClassName.method_name(self) to call the parent version. Method resolution selects the derived implementation when it exists. Polymorphism lets one method call work with multiple derived classes while producing behavior appropriate to each object.

Key Takeaways

  • Base classes define shared attributes and methods; derived classes inherit and specialize them.
  • A derived class should call the parent __init__ method to initialize inherited attributes.
  • Overriding lets a subclass replace or extend an inherited method.
  • Calling the parent method from an override preserves shared behavior without duplicating it.
  • Polymorphism allows the same method call to select different derived implementations.