Python Inheritance: When to Use It and Skip It
Table of Contents
Inheritance is one of those OOP tools that feels magical on day one and dangerous on day thirty. Used well, it removes duplication and keeps models clean. Used badly, it creates class trees no one wants to touch.
All the content from our Boot.dev courses are available for free here on the blog. This one is the “Inheritance” chapter of Learn Object Oriented Programming in Python. If you want to try the far more immersive version of the course, do check it out!
What Is Inheritance in Python?
In inheritance, a child class gets behavior from a parent class. In Python, you declare that relationship with parentheses after the child class name.
class Aircraft:
def __init__(self, height, speed):
self.height = height
self.speed = speed
def fly_up(self):
self.height += self.speed
class Helicopter(Aircraft):
def __init__(self, height, speed):
super().__init__(height, speed)
self.direction = 0
def rotate(self):
self.direction += 90
news_copter = Helicopter(100, 20)
news_copter.fly_up()
news_copter.rotate()
print(news_copter.height, news_copter.direction) # 120 90
Helicopter reuses Aircraft.fly_up() and adds its own rotate() behavior. That’s the core value: reuse plus specialization.
If this still feels fuzzy, read Python classes and objects first, then come back.
How Does super() Work in Child Classes?
super() gives your child class access to methods on its parent. Most of the time, you use it in __init__ so parent setup always runs.
class Hero:
def __init__(self, name, health):
self.name = name
self.health = health
class Wizard(Hero):
def __init__(self, name, health, mana):
super().__init__(name, health)
self.mana = mana
gandalf = Wizard("Gandalf", 120, 250)
print(gandalf.name, gandalf.health, gandalf.mana) # Gandalf 120 250
Without super(), you’ll duplicate parent setup in every child. That’s where bugs creep in fast.
When Should a Class Inherit?
Use this rule: class A should inherit from class B only if A is always a B.
Catis always anAnimal.Truckis always aVehicle.Squareis always aRectanglein many teaching models.
Bad inheritance usually starts when you force a relationship to share a little code. If two classes share one helper method but don’t share identity, inheritance is the wrong tool.
When that happens, use regular functions or composition instead. This is the same design pressure discussed in Python clean code and DRY tradeoffs.
What Makes a Good Inheritance Hierarchy?
A good child class is a strict subset of its parent. It keeps parent promises and adds narrower behavior.
class Archer(Hero):
def __init__(self, name, health, arrows):
super().__init__(name, health)
self.__arrows = arrows
def shoot(self, target):
if self.__arrows <= 0:
raise Exception("not enough arrows")
self.__arrows -= 1
target.health -= 10
class Wizard(Hero):
def __init__(self, name, health, mana):
super().__init__(name, health)
self.__mana = mana
Both Archer and Wizard are heroes. That’s a clean parent/child relationship.
This “group data and behavior” mindset will feel familiar if you’ve already worked through encapsulation and abstraction.
Why Are Wide Trees Better Than Deep Trees?
Most inheritance in (good) production code is wide, not deep.
Deep chains mean a child must satisfy every parent assumption all the way up the tree. That gets brittle. Wide trees are usually easier:
- One stable base class
- Many sibling classes
- Small, focused overrides
If you find yourself building Camera -> Device -> Electronic -> InventoryItem -> GameEntity -> Object, stop and reevaluate. You probably want composition.
How Do Sibling Classes Share One Parent?
Siblings inherit from the same parent and reuse shared behavior while implementing different actions.
class Hero:
def __init__(self, name, health):
self.name = name
self.health = health
class Archer(Hero):
def shoot(self, target):
target.health -= 10
class Wizard(Hero):
def cast(self, target):
target.health -= 25
legolas = Archer("Legolas", 100)
saruman = Wizard("Saruman", 100)
legolas.shoot(saruman)
print(saruman.health) # 90
saruman.cast(legolas)
print(legolas.health) # 75
That gives you a clean family of related types, which also sets up chapter 6’s polymorphism nicely.
When Should You Prefer Composition Over Inheritance?
If a class only needs one small behavior from another type, composition is usually cleaner than inheritance.
class DamageCalculator:
def weapon_hit(self, attack, defense):
return max(0, attack - defense)
class Warrior:
def __init__(self, name, attack, defense):
self.name = name
self.attack = attack
self.defense = defense
self.damage_calculator = DamageCalculator()
def hit(self, target):
return self.damage_calculator.weapon_hit(self.attack, target.defense)
Warrior doesn’t need to be a DamageCalculator. It only needs to use one. That’s exactly where composition shines.
What Should You Learn After Python Inheritance?
After inheritance, the next big step is polymorphism. That’s where shared method names across related classes start paying off in a huge way.
Keep going in Learn Object Oriented Programming in Python. If you want to balance OOP with another mental model, pair it with Learn Functional Programming in Python. For long-term backend growth, follow the Back-end Developer Path in Python and Go.
Frequently Asked Questions
What is inheritance in Python?
Inheritance lets a child class reuse data and methods from a parent class, then add or override behavior.
When should I use inheritance?
Use inheritance when the child is always a strict type of the parent and needs most of the parent behavior.
What does super() do in Python?
super() gives access to parent-class methods, most commonly the parent constructor, so child setup can reuse parent logic.
Is deep inheritance a good idea?
Usually no. Most real systems are easier to maintain with wide, shallow hierarchies instead of deep chains.
Should I use inheritance or composition?
If you only need to share a small piece of behavior, composition is often cleaner. Use inheritance when the is-a relationship is always true.
Related Articles
Clean Code in Python: Write Readable, Maintainable Code
Mar 20, 2026 by Lane Wagner - Boot.dev co-founder and backend engineer
If you ask ten developers for a definition of clean code, you will get twelve answers and one argument in the comments. Still, most of us agree on the practical goal: write code that other humans can understand quickly and change safely. That matters way more than writing clever one-liners that only make sense to your past self at 2 AM.
Python Classes and Objects: A Practical Beginner Guide
Mar 20, 2026 by Lane Wagner - Boot.dev co-founder and backend engineer
If classes and objects still feel slippery, you’re not crazy. Most tutorials either go too abstract or drown you in weird examples. The practical way to learn OOP is simple: understand how data and behavior stick together, then build small objects that are easy to reason about.
Python Encapsulation vs Abstraction: What Matters
Mar 20, 2026 by Lane Wagner - Boot.dev co-founder and backend engineer
If classes and objects are the “what” of OOP, encapsulation and abstraction are the “how.” They’re how you keep code understandable after the project gets big, your team grows, and six months pass. They sound similar because they’re close cousins, but they solve slightly different pains.
Python Polymorphism: One Interface, Many Behaviors
Mar 20, 2026 by Lane Wagner - Boot.dev co-founder and backend engineer
Polymorphism is where OOP starts to feel truly powerful. You stop writing giant if type == ... trees and start trusting shared interfaces. Different objects respond to the same method call in different ways, and your calling code stays clean.