Table of Contents
What Is Inheritance?
Inheritance lets you create a new class based on an existing class.
- The existing class is usually called the parent class, base class, or superclass.
- The new class is called the child class, subclass, or derived class.
The child class inherits attributes and methods from the parent class, and can:
- Use them as they are.
- Change (override) some of them.
- Add new ones.
This avoids writing the same code again and again when classes are similar.
In Python, you define a child class like this:
class Parent:
pass
class Child(Parent):
pass
Here, Child inherits from Parent.
A Simple Inheritance Example
Imagine a general Animal class and more specific animals like Dog and Cat.
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
print("Some generic animal sound")
class Dog(Animal):
pass
class Cat(Animal):
pass
dog = Dog("Buddy")
cat = Cat("Misty")
dog.speak() # Inherited from Animal
cat.speak() # Inherited from Animal
Dog and Cat did not define __init__ or speak, but they can still use them because they inherit from Animal.
Adding New Behavior in the Child Class
A child class can have extra attributes or methods that the parent does not have.
class Animal:
def __init__(self, name):
self.name = name
def info(self):
print(f"I am an animal named {self.name}")
class Dog(Animal):
def bark(self):
print("Woof!")
dog = Dog("Rex")
dog.info() # from Animal
dog.bark() # only in DogThe child class keeps everything from the parent and adds more.
Overriding Methods
Sometimes you want a child class to change how a method works.
This is called overriding a method.
class Animal:
def speak(self):
print("Some generic animal sound")
class Dog(Animal):
def speak(self):
print("Woof!")
class Cat(Animal):
def speak(self):
print("Meow!")
dog = Dog()
cat = Cat()
dog.speak() # Woof!
cat.speak() # Meow!
Here, Dog and Cat both override speak.
When you call speak on a Dog, you get the Dog version, not the Animal one.
Using `super()` to Reuse Parent Code
Sometimes you want to extend a parent method, not completely replace it.
You can call the parent’s version using super().
This is common with __init__.
class Animal:
def __init__(self, name):
self.name = name
class Dog(Animal):
def __init__(self, name, breed):
# Call Animal.__init__(self, name)
super().__init__(name)
self.breed = breed
dog = Dog("Rex", "Labrador")
print(dog.name) # from Animal
print(dog.breed) # from DogWhat happens:
super().__init__(name)runs the parent’s__init__to setself.name.- Then the child class adds
self.breed.
You can also use super() inside other methods:
class Animal:
def info(self):
print("I am an animal.")
class Dog(Animal):
def info(self):
super().info() # Call parent version
print("I am also a dog.")
dog = Dog()
dog.info()
# I am an animal.
# I am also a dog.Inheritance vs. Just Using Attributes
You might wonder: why not just give every class the same attributes and methods manually?
Without inheritance:
class Dog:
def __init__(self, name):
self.name = name
def speak(self):
print("Woof!")
class Cat:
def __init__(self, name):
self.name = name
def speak(self):
print("Meow!")
Here, __init__ is duplicated. If you need to change how names are stored, you must change it in multiple places.
With inheritance:
class Animal:
def __init__(self, name):
self.name = name
class Dog(Animal):
def speak(self):
print("Woof!")
class Cat(Animal):
def speak(self):
print("Meow!")
Now, the shared part (__init__) lives in one place (Animal), and Dog and Cat only define what is different.
“Is-a” Relationship
A helpful rule: use inheritance when the relationship is “is-a”.
- A
Dogis anAnimal→ inheritance makes sense. - A
Caris aVehicle→ inheritance can make sense.
But if the relationship is more like “has-a”, you usually do not use inheritance.
For example:
- A
Carhas an engine (it doesn’t “is-a” engine). - A
Personhas an address.
Those are usually better handled with attributes (one object containing another), not inheritance.
(This idea—using objects inside other objects—is usually called composition, and can be explored separately.)
Basic Inheritance Example: Shapes
Here is a small example with a parent Shape and two child classes.
class Shape:
def area(self):
# Generic shape has no defined area
return 0
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
# Override parent method
return self.width * self.height
class Square(Rectangle):
def __init__(self, side):
# A square is a rectangle with equal sides
super().__init__(side, side)
rect = Rectangle(3, 4)
square = Square(5)
print(rect.area()) # 12
print(square.area()) # 25What this shows:
RectangleandSquareboth inherit fromShape.Rectangleoverridesarea.Squarereuses theRectangle__init__viasuper().
When (and When Not) to Use Inheritance
Use inheritance when:
- You have a clear “is-a” relationship.
- Classes share common behavior that should live in one place.
- Child classes need to specialize or extend the parent’s behavior.
Be careful with inheritance when:
- You are only using it to “reuse code” with no real “is-a” meaning.
- The child class ends up very different from the parent.
- There are many levels of inheritance that become hard to follow.
In those cases, it may be simpler to use normal attributes (objects containing other objects) instead of inheritance.
Small Practice Ideas
You can try building simple class trees like:
Vehicle→Car,BikePerson→Student,TeacherAccount→SavingsAccount,CheckingAccount
For each:
- Put shared attributes and methods in the parent class.
- Add specific behavior in each child class.
- Override at least one method in a child class.
- Optionally, use
super()in a child__init__.