Inheritance and Polymorphism
Sharing behavior between classes with inheritance, overriding methods, and using super().
2 min read
Inheritance lets one class build on another, reusing its behavior instead of duplicating it. Polymorphism is the payoff: code written against a general type can work with any of its more specific subtypes without modification.
Basic inheritance
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return f"{self.name} makes a sound."
class Dog(Animal):
def speak(self):
return f"{self.name} barks."
class Cat(Animal):
def speak(self):
return f"{self.name} meows."Dog(Animal) means Dog inherits from Animal — it automatically gets Animal's __init__ and any other methods, and can override the ones it needs to behave differently.
dog = Dog("Rex")
cat = Cat("Whiskers")
print(dog.speak()) # Rex barks.
print(cat.speak()) # Whiskers meows.Neither Dog nor Cat redefined __init__ — they inherited Animal's version automatically, since they didn't need to change how a name gets stored.
Polymorphism
Because Dog and Cat are both Animals, code that only knows about the Animal interface can work with either without caring which one it actually has:
animals = [Dog("Rex"), Cat("Whiskers"), Dog("Fido")]
for animal in animals:
print(animal.speak())Each call to .speak() runs the version defined on that object's actual class — this is polymorphism: the same method call produces different behavior depending on the object it's called on, and the calling code never needs an if isinstance(animal, Dog) check to make that happen.
Extending, not just overriding, with super()
Sometimes a subclass needs to add to its parent's behavior rather than fully replace it. super() gives access to the parent class's version of a method:
class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary
class Manager(Employee):
def __init__(self, name, salary, team_size):
super().__init__(name, salary) # let Employee set name and salary
self.team_size = team_size
m = Manager("Ada", 95000, 5)
print(m.name, m.salary, m.team_size) # Ada 95000 5Without super().__init__(...), Manager would need to duplicate Employee's assignment logic itself. super() reuses it and just adds what's new.
Checking type relationships
print(isinstance(m, Employee)) # True -- Manager is-a Employee
print(isinstance(m, Manager)) # True
print(type(m) is Manager) # True
print(type(m) is Employee) # False -- exact type, not hierarchyisinstance() respects inheritance (a Manager counts as an Employee); type() is checks for an exact match only. Prefer isinstance() in most real code — it's what lets polymorphism work the way it's meant to, treating subclasses as fully valid instances of their parent type.
Test what you just learned
4 quick questions. Get all of them right to unlock the next lesson.
You can take the quiz without an account — logging in just lets your result count toward your progress.