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
Interactive lab

Try it: Inheritance and Method Lookup

How a subclass inherits from its parent, how Python finds an attribute by walking the instance, then the class's method resolution order up to object, how overriding and super() work, why the same call does different things on different objects (polymorphism), and what isinstance / issubclass check.

How it works

  1. class Dog(Animal) gives Dog the MRO Dog → Animal → object; Puppy(Dog) gives Puppy → Dog → Animal → object.
  2. obj.name searches obj.__dict__, then each class of type(obj).__mro__ in order; the first class that defines the name wins, so a subclass method overrides the parent's.
  3. super().method() starts the search in the class after the one the current method was defined in, e.g. to run the parent's __init__.
  4. Calling the same method on a list of different objects runs each object's own version (polymorphism).
  5. isinstance(obj, C) is True when C is in type(obj).__mro__; if no class has the name, Python raises AttributeError.

Default run (63 steps): Ready: Animal, Dog, Cat and Puppy, then the statements you picked. Each attribute is searched along the class's MRO. … Finished. Printed: Rex the dog says Woof / Tom the cat says Meow / Bo the dog says Woof! / True / Puppy / Dog / Animal / object.

Simplified: The lab's own small Python interpreter runs a fixed, hand-written script (you choose the variant and the values, never the code); nothing is executed as real Python. Every script was run in CPython 3.12 with the same inputs and the lab reproduces its output and error messages. Single inheritance only (the lab's MRO code is Python's C3 algorithm, but no class here has two parents).

Educational simulation

Loading the simulation…

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.

member.tell()same callTeacherTeacher.tell()Teacher detailsname, age, salaryStudentStudent.tell()Student detailsname, age, marksobject is Teacherobject is Studentrunsruns
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.

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

Separating Shared and Specialized Data

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

  1. 1
    Create the base class

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

  2. 2
    Create the teacher subclass

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

  3. 3
    Create the student subclass

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

  4. 4
    Use the inherited features

    A Teacher object and a Student object both have name and age because those attributes come from SchoolMember.

Result

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.

Teacher(...)object creationTeacher.__init__startsSchoolMember.__init__creates name, agesalarycreated by TeacherTeacher objectname, age, salarycallsexplicitly callsreturns tofinishes
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.tell()method callTeacher objectsubclass method existsTeacher.tell()runs firstSchoolMember.tell()called explicitly whenneededStudent objectsubclass method existsStudent.tell()runs firstobject is Teacherobject is Studentresolves toresolves tomay callmay call
When the same method is defined in both a base class and a subclass, which implementation runs for a subclass object?
Before
SchoolMember.tell(self)

The inherited method supplies the shared name and age details.

After
SchoolMember.tell(self)
print salary or marks

The derived method keeps the shared details and adds specialized information.

Best practice

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.

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

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

SchoolMembershared definitionnameinherited attributeTeacheradds salaryageinherited attributeStudentadds markstell()inherited methoddefinesdefinesdefinesreused byreused byinherited or overridden byinherited or overridden by
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

The subclasses reuse common features while adding or customizing behavior.

Mistakes with Inheritance

  • Common mistake

    Skipping the parent __init__ call

    What it looks like

    A derived constructor initializes salary or marks but never calls SchoolMember.__init__(self, name, age).

    Why it fails

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

    Do this instead

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

  • Common mistake

    Assuming the base tell() method always runs

    What it looks like

    A Teacher object defines tell(), but the programmer expects SchoolMember.tell() to be selected automatically.

    Why it fails

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

    Do this instead

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

  • Common mistake

    Duplicating shared attributes in every subclass

    What it looks like

    Teacher and Student each define their own copies of name and age instead of inheriting them.

    Why it fails

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

    Do this instead

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

  • Common mistake

    Checking every concrete type before calling tell()

    What it looks like

    A loop branches separately for Teacher and Student instead of calling member.tell().

    Why it fails

    The code gives up the flexibility provided by polymorphism.

    Do this instead

    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.
Worked example

Tracing Two Objects

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

  1. 1
    First iteration

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

  2. 2
    Reuse shared behavior

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

  3. 3
    Add teacher behavior

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

  4. 4
    Second iteration

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

  5. 5
    Add student behavior

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

Result

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. 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.

At a glance

  • 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.

Related concepts

More concepts starting with P