Welcome again, @name! Darwin here with a fascinating topic.
In previous lessons you learned to create classes representing species. But in nature, species are not isolated - they are part of a taxonomic hierarchy! Lions and tigers are different species, but both are felines. Felines and canines are different families, but both are mammals.
In object-oriented programming, this hierarchy is called inheritance - one of the most powerful OOP mechanisms!
Inheritance allows creating new classes based on existing ones:
The derived class inherits attributes and methods from the base class, and can then:
1# Base class - general Mammal
2class Mammal:
3 def __init__(self, name):
4 self.name = name
5
6 def breathe(self):
7 return f"{self.name} breathes air"
8
9 def move(self):
10 return f"{self.name} moves"
11
12# Derived class - Lion inherits from Mammal
13class Lion(Mammal):
14 def __init__(self, name, pride_name):
15 super().__init__(name) # Call parent constructor
16 self.pride_name = pride_name # Add own attribute
17
18 def roar(self):
19 """New method - only for lions"""
20 return f"{self.name} roars: RRROAAR!"
21
22 def move(self):
23 """Override parent method"""
24 return f"{self.name} runs across the savanna"
25
26# Usage
27simba = Lion("Simba", "Pride Rock")
28
29# Inherited from Mammal
30print(simba.breathe()) # "Simba breathes air"
31
32# Overridden in Lion
33print(simba.move()) # "Simba runs across the savanna"
34
35# Own Lion method
36print(simba.roar()) # "Simba roars: RRROAAR!"
37
38# Own Lion attribute
39print(simba.pride_name) # "Pride Rock"1# Basic syntax
2class ChildClass(ParentClass):
3 pass
4
5# Inheritance with extension
6class Animal:
7 def eat(self):
8 return "I eat"
9
10class Dog(Animal): # Dog inherits from Animal
11 def bark(self):
12 return "Woof woof!"
13
14dog = Dog()
15print(dog.eat()) # Inherited: "I eat"
16print(dog.bark()) # Own: "Woof woof!"super() function
allows referring to the base class - use it to:super()
1class Animal:
2 def __init__(self, name, age):
3 self.name = name
4 self.age = age
5 print(f"Creating animal: {name}")
6
7 def describe(self):
8 return f"{self.name}, age: {self.age}"
9
10class Bird(Animal):
11 def __init__(self, name, age, wingspan):
12 # Call Animal constructor
13 super().__init__(name, age)
14 # Add own attribute
15 self.wingspan = wingspan
16 print(f"Wingspan: {wingspan}m")
17
18 def describe(self):
19 # Extend parent method
20 base_desc = super().describe() # Call Animal.describe()
21 return f"{base_desc}, wingspan: {self.wingspan}m"
22
23eagle = Bird("Eagle", 5, 2.3)
24# Prints:
25# Creating animal: Eagle
26# Wingspan: 2.3m
27
28print(eagle.describe())
29# "Eagle, age: 5, wingspan: 2.3m"A derived class can completely replace a parent method:
1class Animal:
2 def speak(self):
3 return "Animal makes a sound"
4
5class Dog(Animal):
6 def speak(self):
7 """Override completely"""
8 return "Woof woof!" # Completely new behavior
9
10class Cat(Animal):
11 def speak(self):
12 """Override completely"""
13 return "Meow!"
14
15dog = Dog()
16cat = Cat()
17
18print(dog.speak()) # "Woof woof!" - not "Animal makes a sound"
19print(cat.speak()) # "Meow!"In nature the hierarchy is deep: Kingdom → Phylum → Class → Order → Family → Genus → Species
1# 1. Kingdom Animalia
2class Animal:
3 kingdom = "Animalia"
4
5 def __init__(self, name):
6 self.name = name
7
8 def is_alive(self):
9 return True
10
11 def __str__(self):
12 return f"{self.name} ({self.__class__.__name__})"
13
14# 2. Phylum Chordata
15class Chordate(Animal):
16 phylum = "Chordata"
17
18 def has_backbone(self):
19 return True
20
21# 3. Class Mammalia
22class Mammal(Chordate):
23 class_name = "Mammalia"
24
25 def __init__(self, name, fur_color):
26 super().__init__(name)
27 self.fur_color = fur_color
28
29 def nurse_young(self):
30 return f"{self.name} nurses its young with milk"
31
32 def regulate_temperature(self):
33 return f"{self.name} maintains constant body temperature"
34
35# 4. Order Carnivora
36class Carnivore(Mammal):
37 order = "Carnivora"
38
39 def __init__(self, name, fur_color, hunting_style):
40 super().__init__(name, fur_color)
41 self.hunting_style = hunting_style
42
43 def hunt(self):
44 return f"{self.name} hunts using: {self.hunting_style}"
45
46# 5. Family Felidae
47class Feline(Carnivore):
48 family = "Felidae"
49
50 def retract_claws(self):
51 return f"{self.name} retracts its claws"
52
53 def purr(self):
54 return f"{self.name} purrs"
55
56# 6. Species Panthera leo (Lion)
57class Lion(Feline):
58 species = "Panthera leo"
59
60 def __init__(self, name, fur_color, pride_name):
61 super().__init__(name, fur_color, "pack hunting")
62 self.pride_name = pride_name
63
64 def roar(self):
65 return f"{self.name} roars loudly!"
66
67 def lead_pride(self):
68 return f"{self.name} leads the {self.pride_name} pride"
69
70# Usage - the lion has access to ALL methods in the hierarchy!
71simba = Lion("Simba", "golden", "Pride Rock")
72
73# From Animal
74print(simba.is_alive()) # True
75
76# From Chordate
77print(simba.has_backbone()) # True
78
79# From Mammal
80print(simba.nurse_young()) # "Simba nurses its young with milk"
81print(simba.regulate_temperature()) # "Simba maintains constant body temperature"
82
83# From Carnivore
84print(simba.hunt()) # "Simba hunts using: pack hunting"
85
86# From Feline
87print(simba.retract_claws()) # "Simba retracts its claws"
88print(simba.purr()) # "Simba purrs"
89
90# Own Lion methods
91print(simba.roar()) # "Simba roars loudly!"
92print(simba.lead_pride()) # "Simba leads the Pride Rock pride"
93
94# Attributes
95print(simba.kingdom) # "Animalia"
96print(simba.species) # "Panthera leo"
97print(simba.fur_color) # "golden"Python offers functions to check the hierarchy:
1class Animal:
2 pass
3
4class Mammal(Animal):
5 pass
6
7class Lion(Mammal):
8 pass
9
10simba = Lion()
11
12# isinstance() - check if object is an instance of a class
13print(isinstance(simba, Lion)) # True
14print(isinstance(simba, Mammal)) # True - Lion inherits from Mammal
15print(isinstance(simba, Animal)) # True - Lion inherits from Animal (through Mammal)
16print(isinstance(simba, str)) # False
17
18# issubclass() - check if a class inherits from another class
19print(issubclass(Lion, Mammal)) # True
20print(issubclass(Lion, Animal)) # True
21print(issubclass(Mammal, Lion)) # False
22print(issubclass(Lion, Lion)) # True - a class is a subclass of itself
23
24# type() - return the exact type
25print(type(simba)) # <class '__main__.Lion'>
26print(type(simba) == Lion) # True
27print(type(simba) == Mammal) # False - simba is a Lion, not a MammalPython allows inheriting from multiple classes at once - a class can have multiple parents!
1class Swimmer:
2 def swim(self):
3 return f"{self.name} swims"
4
5class Flyer:
6 def fly(self):
7 return f"{self.name} flies"
8
9class Walker:
10 def walk(self):
11 return f"{self.name} walks"
12
13# Duck inherits from three classes!
14class Duck(Swimmer, Flyer, Walker):
15 def __init__(self, name):
16 self.name = name
17
18 def quack(self):
19 return f"{self.name}: Quack quack!"
20
21donald = Duck("Donald")
22
23# Has access to methods from all base classes
24print(donald.swim()) # "Donald swims"
25print(donald.fly()) # "Donald flies"
26print(donald.walk()) # "Donald walks"
27print(donald.quack()) # "Donald: Quack quack!"When a class inherits from multiple parents, Python uses MRO (Method Resolution Order) to determine the method search order:
1class A:
2 def method(self):
3 return "A"
4
5class B(A):
6 def method(self):
7 return "B"
8
9class C(A):
10 def method(self):
11 return "C"
12
13class D(B, C): # Inherits from B and C
14 pass
15
16d = D()
17print(d.method()) # "B" - Python searches: D → B → C → A
18
19# Check MRO
20print(D.__mro__)
21# (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>,
22# <class '__main__.A'>, <class 'object'>)
23
24# Or more readable:
25print(D.mro())Rule: Python searches classes from left to right, from most specific to most general.
1class Species:
2 """
3 Base class for all species in the Safari catalog
4 """
5
6 kingdom = "Animalia"
7 total_species_count = 0
8
9 def __init__(self, scientific_name, common_name, habitat):
10 self.scientific_name = scientific_name
11 self.common_name = common_name
12 self.habitat = habitat
13 self.observations = []
14
15 Species.total_species_count += 1
16
17 def add_observation(self, date, location, count, notes=""):
18 """Add a field observation"""
19 self.observations.append({
20 "date": date,
21 "location": location,
22 "count": count,
23 "notes": notes
24 })
25
26 def get_total_observed(self):
27 """Total number of observed individuals"""
28 return sum(obs["count"] for obs in self.observations)
29
30 def describe(self):
31 """Basic species description"""
32 return f"{self.common_name} ({self.scientific_name})"
33
34 def __str__(self):
35 return self.describe()
36
37class Mammal(Species):
38 """
39 Class representing mammals - extends Species
40 """
41
42 class_name = "Mammalia"
43
44 def __init__(self, scientific_name, common_name, habitat, fur_color, gestation_days):
45 # Call Species constructor
46 super().__init__(scientific_name, common_name, habitat)
47 # Add mammal-specific attributes
48 self.fur_color = fur_color
49 self.gestation_days = gestation_days
50
51 def nurse_young(self):
52 """Mammals nurse their young with milk"""
53 return f"{self.common_name} nurses its young for {self.gestation_days // 30} months"
54
55 def describe(self):
56 """Extend description with mammal information"""
57 base = super().describe() # Call Species.describe()
58 return f"{base} | Mammal | Fur: {self.fur_color}"
59
60class Bird(Species):
61 """
62 Class representing birds
63 """
64
65 class_name = "Aves"
66
67 def __init__(self, scientific_name, common_name, habitat, wingspan_m, can_fly=True):
68 super().__init__(scientific_name, common_name, habitat)
69 self.wingspan_m = wingspan_m
70 self.can_fly = can_fly
71
72 def lay_eggs(self):
73 return f"{self.common_name} lays eggs"
74
75 def describe(self):
76 base = super().describe()
77 flight_status = "flying" if self.can_fly else "flightless"
78 return f"{base} | {flight_status} Bird | Wingspan: {self.wingspan_m}m"
79
80class Carnivore(Mammal):
81 """
82 Predators - inherit from Mammal
83 """
84
85 order = "Carnivora"
86
87 def __init__(self, scientific_name, common_name, habitat, fur_color,
88 gestation_days, hunting_style, pack_hunter=False):
89 super().__init__(scientific_name, common_name, habitat, fur_color, gestation_days)
90 self.hunting_style = hunting_style
91 self.pack_hunter = pack_hunter
92
93 def hunt(self):
94 style = "in a pack" if self.pack_hunter else "alone"
95 return f"{self.common_name} hunts {style} using: {self.hunting_style}"
96
97 def calculate_danger_level(self):
98 """Calculate danger level (1-10)"""
99 base_danger = 5
100 if self.pack_hunter:
101 base_danger += 3
102 if "ambush" in self.hunting_style:
103 base_danger += 2
104 return min(10, base_danger)
105
106 def describe(self):
107 base = super().describe()
108 danger = self.calculate_danger_level()
109 return f"{base} | Predator | Danger: {danger}/10"
110
111class Herbivore(Mammal):
112 """
113 Herbivores - inherit from Mammal
114 """
115
116 def __init__(self, scientific_name, common_name, habitat, fur_color,
117 gestation_days, diet_type):
118 super().__init__(scientific_name, common_name, habitat, fur_color, gestation_days)
119 self.diet_type = diet_type # "leaves", "grass", "fruit"
120
121 def graze(self):
122 return f"{self.common_name} eats {self.diet_type}"
123
124 def describe(self):
125 base = super().describe()
126 return f"{base} | Herbivore | Diet: {self.diet_type}"
127
128class Feline(Carnivore):
129 """
130 Felines - inherit from Carnivore
131 """
132
133 family = "Felidae"
134
135 def __init__(self, scientific_name, common_name, habitat, fur_color,
136 gestation_days, has_mane=False, pride_size=1):
137 super().__init__(
138 scientific_name, common_name, habitat, fur_color,
139 gestation_days, "ambush and chase", pack_hunter=(pride_size > 1)
140 )
141 self.has_mane = has_mane
142 self.pride_size = pride_size
143
144 def retract_claws(self):
145 return f"{self.common_name} retracts its sharp claws"
146
147 def stalk_prey(self):
148 return f"{self.common_name} stalks its prey silently"
149
150 def describe(self):
151 base = super().describe()
152 social = f"social ({self.pride_size})" if self.pride_size > 1 else "solitary"
153 return f"{base} | Feline | {social}"
154
155class Canine(Carnivore):
156 """
157 Canines - inherit from Carnivore
158 """
159
160 family = "Canidae"
161
162 def __init__(self, scientific_name, common_name, habitat, fur_color,
163 gestation_days, pack_size=1):
164 super().__init__(
165 scientific_name, common_name, habitat, fur_color,
166 gestation_days, "endurance pursuit", pack_hunter=(pack_size > 1)
167 )
168 self.pack_size = pack_size
169
170 def howl(self):
171 return f"{self.common_name} howls to the pack"
172
173 def track_scent(self):
174 return f"{self.common_name} tracks prey by scent"
175
176 def describe(self):
177 base = super().describe()
178 social = f"pack ({self.pack_size})" if self.pack_size > 1 else "solitary"
179 return f"{base} | Canine | {social}"
180
181# === DEMONSTRATION - Creating Safari hierarchy ===
182
183print("=== SAFARI SPECIES CATALOG - HIERARCHY ===\n")
184
185# Felines
186lion = Feline(
187 scientific_name="Panthera leo",
188 common_name="Lion",
189 habitat="savanna",
190 fur_color="golden",
191 gestation_days=110,
192 has_mane=True,
193 pride_size=15
194)
195
196leopard = Feline(
197 scientific_name="Panthera pardus",
198 common_name="Leopard",
199 habitat="savanna and forests",
200 fur_color="yellow with black spots",
201 gestation_days=90,
202 has_mane=False,
203 pride_size=1 # Solitary
204)
205
206# Canines
207wild_dog = Canine(
208 scientific_name="Lycaon pictus",
209 common_name="African Wild Dog",
210 habitat="savanna",
211 fur_color="mottled",
212 gestation_days=70,
213 pack_size=20
214)
215
216# Herbivores
217elephant = Herbivore(
218 scientific_name="Loxodonta africana",
219 common_name="African Elephant",
220 habitat="savanna",
221 fur_color="gray",
222 gestation_days=645, # Longest gestation!
223 diet_type="leaves, bark, grass"
224)
225
226giraffe = Herbivore(
227 scientific_name="Giraffa camelopardalis",
228 common_name="Giraffe",
229 habitat="savanna",
230 fur_color="yellow with brown patches",
231 gestation_days=440,
232 diet_type="acacia leaves"
233)
234
235# Birds
236eagle = Bird(
237 scientific_name="Aquila rapax",
238 common_name="Tawny Eagle",
239 habitat="savanna",
240 wingspan_m=2.1,
241 can_fly=True
242)
243
244# Add observations
245lion.add_observation("2024-01-15", "Serengeti", 12, "Pride hunting wildebeest")
246lion.add_observation("2024-01-20", "Masai Mara", 8, "Family with cubs")
247leopard.add_observation("2024-01-16", "Rocky area", 1, "Solitary male")
248wild_dog.add_observation("2024-01-18", "Savuti", 18, "Pack hunting")
249elephant.add_observation("2024-01-17", "Amboseli", 45, "Large herd at waterhole")
250giraffe.add_observation("2024-01-19", "Tarangire", 23, "Giraffes feeding on acacias")
251eagle.add_observation("2024-01-21", "Sky over savanna", 3, "Pair with juvenile")
252
253# Display descriptions - each uses its own version of describe()
254print("🦁 LION:")
255print(f" {lion.describe()}")
256print(f" {lion.hunt()}")
257print(f" {lion.stalk_prey()}")
258print(f" {lion.retract_claws()}")
259print(f" {lion.nurse_young()}")
260print(f" Observations: {lion.get_total_observed()} individuals")
261
262print(f"\n🐆 LEOPARD:")
263print(f" {leopard.describe()}")
264print(f" {leopard.hunt()}")
265print(f" Danger level: {leopard.calculate_danger_level()}/10")
266
267print(f"\n🐺 AFRICAN WILD DOG:")
268print(f" {wild_dog.describe()}")
269print(f" {wild_dog.hunt()}")
270print(f" {wild_dog.howl()}")
271print(f" {wild_dog.track_scent()}")
272
273print(f"\n🐘 ELEPHANT:")
274print(f" {elephant.describe()}")
275print(f" {elephant.graze()}")
276print(f" Observations: {elephant.get_total_observed()} individuals")
277
278print(f"\n🦒 GIRAFFE:")
279print(f" {giraffe.describe()}")
280print(f" {giraffe.graze()}")
281
282print(f"\n🦅 EAGLE:")
283print(f" {eagle.describe()}")
284print(f" {eagle.lay_eggs()}")
285
286# Check hierarchy
287print(f"\n=== CLASS HIERARCHY ===")
288print(f"Lion inherits from Feline? {isinstance(lion, Feline)}")
289print(f"Lion inherits from Carnivore? {isinstance(lion, Carnivore)}")
290print(f"Lion inherits from Mammal? {isinstance(lion, Mammal)}")
291print(f"Lion inherits from Species? {isinstance(lion, Species)}")
292print(f"Lion inherits from Bird? {isinstance(lion, Bird)}")
293
294print(f"\nMRO Feline: {[cls.__name__ for cls in Feline.mro()]}")
295# ['Feline', 'Carnivore', 'Mammal', 'Species', 'object']
296
297print(f"\n=== STATISTICS ===")
298print(f"Total species in catalog: {Species.total_species_count}")
299
300# Polymorphism - each species has its own implementation of describe()
301print(f"\nAll species:")
302all_animals = [lion, leopard, wild_dog, elephant, giraffe, eagle]
303for animal in all_animals:
304 print(f" - {animal.describe()}")✅ Use inheritance when:
❌ DON'T use inheritance when:
1# ✅ Good - "is-a" relationship
2class Animal:
3 pass
4
5class Dog(Animal): # Dog IS an animal
6 pass
7
8# ❌ Bad - "has-a" relationship
9class Engine:
10 def start(self):
11 return "Engine started"
12
13class Car(Engine): # NO! Car IS NOT an engine!
14 pass
15
16# ✅ Good - composition
17class Car:
18 def __init__(self):
19 self.engine = Engine() # Car HAS an engine
20
21 def start(self):
22 return self.engine.start()An object of a derived class should be able to replace an object of the base class without changing program correctness.
1def make_sound(animal):
2 """Function accepts any Animal"""
3 print(animal.speak())
4
5class Animal:
6 def speak(self):
7 return "..."
8
9class Dog(Animal):
10 def speak(self):
11 return "Woof!"
12
13class Cat(Animal):
14 def speak(self):
15 return "Meow!"
16
17# LSP - Dog and Cat can replace Animal
18make_sound(Dog()) # "Woof!" - works
19make_sound(Cat()) # "Meow!" - works1# ❌ Bad - hierarchy too deep
2class A:
3 pass
4class B(A):
5 pass
6class C(B):
7 pass
8class D(C):
9 pass
10class E(D):
11 pass
12class F(E): # 6 levels!
13 pass
14
15# ✅ Good - flat, understandable hierarchy
16class Animal:
17 pass
18
19class Mammal(Animal):
20 pass
21
22class Lion(Mammal):
23 pass
24# 3 levels - readable!In this lesson you learned:
class Child(Parent)super() to call parent methodsisinstance() and issubclass()Before moving on:
super() in a constructorSafari Analogy: A class hierarchy is an evolutionary tree - each descendant inherits traits from ancestors but develops its own unique adaptations! 🌳🦁🐘
In the next lesson Darwin will teach you encapsulation - how to protect sensitive species data from unauthorized access! 🔒📊