+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Part 130 of 365

๐Ÿ“˜ Inheritance: Creating Subclasses

Master inheritance: creating subclasses in Python with practical examples, best practices, and real-world applications ๐Ÿš€

๐Ÿš€Intermediate
25 min read

Prerequisites

  • Basic understanding of programming concepts ๐Ÿ“
  • Python installation (3.8+) ๐Ÿ
  • VS Code or preferred IDE ๐Ÿ’ป

What you'll learn

  • Understand the concept fundamentals ๐ŸŽฏ
  • Apply the concept in real projects ๐Ÿ—๏ธ
  • Debug common issues ๐Ÿ›
  • Write clean, Pythonic code โœจ

๐ŸŽฏ Introduction

Welcome to this exciting tutorial on inheritance in Python! ๐ŸŽ‰ In this guide, weโ€™ll explore how to create subclasses and harness the power of object-oriented programming.

Youโ€™ll discover how inheritance can transform your Python development experience by letting you build on existing code like stacking LEGO blocks! ๐Ÿงฑ Whether youโ€™re building web applications ๐ŸŒ, game engines ๐ŸŽฎ, or data analysis tools ๐Ÿ“Š, understanding inheritance is essential for writing elegant, reusable code.

By the end of this tutorial, youโ€™ll feel confident creating class hierarchies that make your code more organized and powerful! Letโ€™s dive in! ๐ŸŠโ€โ™‚๏ธ

๐Ÿ“š Understanding Inheritance

๐Ÿค” What is Inheritance?

Inheritance is like a family tree ๐ŸŒณ. Think of it as children inheriting traits from their parents - your eye color, your height, or your love for pizza! ๐Ÿ• In programming, classes can inherit attributes and methods from other classes.

In Python terms, inheritance allows you to create new classes based on existing ones. This means you can:

  • โœจ Reuse code without copying and pasting
  • ๐Ÿš€ Extend functionality by adding new features
  • ๐Ÿ›ก๏ธ Override methods to customize behavior
  • ๐ŸŽฏ Create specialized versions of general classes

๐Ÿ’ก Why Use Inheritance?

Hereโ€™s why developers love inheritance:

  1. DRY Principle ๐Ÿ”’: Donโ€™t Repeat Yourself - write once, use everywhere
  2. Code Organization ๐Ÿ’ป: Create logical hierarchies that mirror real-world relationships
  3. Extensibility ๐Ÿ“–: Easily add new features without breaking existing code
  4. Polymorphism ๐Ÿ”ง: Use different objects through the same interface

Real-world example: Imagine building a game ๐ŸŽฎ. You have a general Character class, and you want to create Hero, Villain, and NPC classes. Instead of writing everything from scratch, inheritance lets you build on the base Character class!

๐Ÿ”ง Basic Syntax and Usage

๐Ÿ“ Simple Example

Letโ€™s start with a friendly example:

# ๐Ÿ‘‹ Hello, Inheritance!
class Animal:
    """๐Ÿพ Base class for all animals"""
    def __init__(self, name, age):
        self.name = name    # ๐Ÿท๏ธ Animal's name
        self.age = age      # ๐ŸŽ‚ Animal's age
    
    def speak(self):
        """๐Ÿ—ฃ๏ธ Make the animal speak"""
        return f"{self.name} makes a sound!"
    
    def birthday(self):
        """๐ŸŽ‰ Celebrate birthday!"""
        self.age += 1
        return f"Happy birthday {self.name}! Now {self.age} years old! ๐ŸŽ‚"

# ๐Ÿ• Dog inherits from Animal
class Dog(Animal):
    def __init__(self, name, age, breed):
        super().__init__(name, age)  # ๐Ÿ“ž Call parent constructor
        self.breed = breed           # ๐Ÿ• Dog-specific attribute
    
    def speak(self):
        """๐Ÿ• Dogs bark!"""
        return f"{self.name} says: Woof woof! ๐Ÿถ"
    
    def fetch(self):
        """๐ŸŽพ Dogs love to fetch"""
        return f"{self.name} is fetching the ball! ๐ŸŽพ"

# ๐ŸŽฎ Let's use it!
buddy = Dog("Buddy", 3, "Golden Retriever")
print(buddy.speak())        # Buddy says: Woof woof! ๐Ÿถ
print(buddy.birthday())     # Happy birthday Buddy! Now 4 years old! ๐ŸŽ‚
print(buddy.fetch())        # Buddy is fetching the ball! ๐ŸŽพ

๐Ÿ’ก Explanation: Notice how Dog inherits from Animal by putting Animal in parentheses. The super() function calls the parent classโ€™s methods. The dog has all animal abilities plus its own special tricks!

๐ŸŽฏ Common Patterns

Here are patterns youโ€™ll use daily:

# ๐Ÿ—๏ธ Pattern 1: Multi-level inheritance
class Mammal(Animal):
    """๐Ÿฆ Mammals are warm-blooded animals"""
    def __init__(self, name, age, fur_color):
        super().__init__(name, age)
        self.fur_color = fur_color
    
    def feed_young(self):
        """๐Ÿผ Mammals feed their babies milk"""
        return f"{self.name} is feeding its young! ๐Ÿผ"

class Cat(Mammal):
    """๐Ÿฑ Cats are independent mammals"""
    def __init__(self, name, age, fur_color, indoor=True):
        super().__init__(name, age, fur_color)
        self.indoor = indoor
    
    def speak(self):
        return f"{self.name} says: Meow! ๐Ÿ˜ธ"
    
    def purr(self):
        return f"{self.name} is purring contentedly... ๐Ÿ˜ป"

# ๐ŸŽจ Pattern 2: Multiple inheritance
class Swimmer:
    """๐ŸŠ Can swim"""
    def swim(self):
        return "Splashing in the water! ๐Ÿ’ฆ"

class Flyer:
    """๐Ÿฆ… Can fly"""
    def fly(self):
        return "Soaring through the sky! ๐ŸŒค๏ธ"

class Duck(Animal, Swimmer, Flyer):
    """๐Ÿฆ† Ducks can do it all!"""
    def speak(self):
        return f"{self.name} says: Quack quack! ๐Ÿฆ†"

# ๐Ÿ”„ Pattern 3: Abstract base classes
from abc import ABC, abstractmethod

class Vehicle(ABC):
    """๐Ÿš— Abstract base for all vehicles"""
    def __init__(self, brand, model):
        self.brand = brand
        self.model = model
    
    @abstractmethod
    def start(self):
        """Every vehicle must implement start"""
        pass
    
    @abstractmethod
    def stop(self):
        """Every vehicle must implement stop"""
        pass

๐Ÿ’ก Practical Examples

๐Ÿ›’ Example 1: E-commerce Product System

Letโ€™s build something real:

# ๐Ÿ›๏ธ Base product class
class Product:
    """๐Ÿ“ฆ Base class for all products"""
    def __init__(self, name, price, stock):
        self.name = name
        self.price = price
        self.stock = stock
        self.reviews = []
    
    def add_review(self, rating, comment):
        """โญ Add customer review"""
        self.reviews.append({
            'rating': rating,
            'comment': comment,
            'emoji': 'โญ' * rating
        })
        return f"Thanks for reviewing {self.name}! {('โญ' * rating)}"
    
    def get_average_rating(self):
        """๐Ÿ“Š Calculate average rating"""
        if not self.reviews:
            return 0
        total = sum(review['rating'] for review in self.reviews)
        return total / len(self.reviews)
    
    def display_info(self):
        """๐Ÿ“‹ Show product information"""
        avg_rating = self.get_average_rating()
        return f"""
        ๐Ÿท๏ธ {self.name}
        ๐Ÿ’ฐ ${self.price}
        ๐Ÿ“ฆ Stock: {self.stock}
        โญ Rating: {avg_rating:.1f}/5 ({len(self.reviews)} reviews)
        """

# ๐Ÿ“ฑ Electronics inherit from Product
class Electronic(Product):
    def __init__(self, name, price, stock, warranty_years, brand):
        super().__init__(name, price, stock)
        self.warranty_years = warranty_years
        self.brand = brand
    
    def display_info(self):
        """๐Ÿ“‹ Enhanced display with warranty info"""
        base_info = super().display_info()
        return base_info + f"        ๐Ÿ›ก๏ธ Warranty: {self.warranty_years} years\n        ๐Ÿญ Brand: {self.brand}"

# ๐Ÿ’ป Specific electronic products
class Laptop(Electronic):
    def __init__(self, name, price, stock, warranty_years, brand, ram_gb, storage_gb):
        super().__init__(name, price, stock, warranty_years, brand)
        self.ram_gb = ram_gb
        self.storage_gb = storage_gb
    
    def upgrade_ram(self, additional_gb):
        """๐Ÿš€ Upgrade laptop RAM"""
        self.ram_gb += additional_gb
        self.price += additional_gb * 50  # $50 per GB
        return f"RAM upgraded! Now {self.ram_gb}GB ๐ŸŽ‰"
    
    def display_info(self):
        """๐Ÿ“‹ Show laptop specs"""
        base_info = super().display_info()
        return base_info + f"        ๐Ÿ’พ RAM: {self.ram_gb}GB\n        ๐Ÿ’ฟ Storage: {self.storage_gb}GB"

# ๐ŸŽฎ Let's use our product system!
gaming_laptop = Laptop(
    "UltraGamer Pro", 1299.99, 15, 2, "TechCorp",
    16, 512
)

# Add some reviews
gaming_laptop.add_review(5, "Amazing performance! ๐Ÿš€")
gaming_laptop.add_review(4, "Great, but a bit heavy")
gaming_laptop.add_review(5, "Best laptop ever! ๐Ÿ’ช")

# Display info
print(gaming_laptop.display_info())

# Upgrade it!
print(gaming_laptop.upgrade_ram(16))  # Upgrade to 32GB

๐ŸŽฏ Try it yourself: Add a Smartphone class with battery life and screen size attributes!

๐ŸŽฎ Example 2: Game Character System

Letโ€™s make it fun:

# ๐ŸŽญ Base character class
class GameCharacter:
    """โš”๏ธ Base class for all game characters"""
    def __init__(self, name, health, damage):
        self.name = name
        self.max_health = health
        self.current_health = health
        self.damage = damage
        self.level = 1
        self.experience = 0
        self.abilities = []
    
    def attack(self, target):
        """โš”๏ธ Basic attack"""
        target.take_damage(self.damage)
        return f"{self.name} attacks {target.name} for {self.damage} damage! ๐Ÿ’ฅ"
    
    def take_damage(self, amount):
        """๐Ÿ›ก๏ธ Take damage"""
        self.current_health -= amount
        if self.current_health <= 0:
            return f"{self.name} has been defeated! ๐Ÿ’€"
        return f"{self.name} takes {amount} damage! HP: {self.current_health}/{self.max_health}"
    
    def heal(self, amount):
        """โค๏ธ Heal character"""
        self.current_health = min(self.current_health + amount, self.max_health)
        return f"{self.name} heals for {amount}! HP: {self.current_health}/{self.max_health} โค๏ธ"
    
    def gain_experience(self, exp):
        """๐Ÿ“ˆ Gain experience and level up"""
        self.experience += exp
        if self.experience >= self.level * 100:
            self.level_up()
        return f"{self.name} gains {exp} EXP! ๐ŸŒŸ"
    
    def level_up(self):
        """๐ŸŽŠ Level up!"""
        self.level += 1
        self.max_health += 20
        self.current_health = self.max_health
        self.damage += 5
        return f"{self.name} leveled up to {self.level}! ๐ŸŽ‰"

# ๐Ÿฆธ Hero class
class Hero(GameCharacter):
    def __init__(self, name, hero_class):
        health_map = {
            "Warrior": 150,
            "Mage": 80,
            "Rogue": 100
        }
        damage_map = {
            "Warrior": 20,
            "Mage": 30,
            "Rogue": 25
        }
        super().__init__(name, health_map[hero_class], damage_map[hero_class])
        self.hero_class = hero_class
        self.mana = 100 if hero_class == "Mage" else 50
        self.init_abilities()
    
    def init_abilities(self):
        """๐ŸŽฏ Initialize class-specific abilities"""
        if self.hero_class == "Warrior":
            self.abilities = ["Shield Bash ๐Ÿ›ก๏ธ", "Whirlwind โš”๏ธ"]
        elif self.hero_class == "Mage":
            self.abilities = ["Fireball ๐Ÿ”ฅ", "Ice Shield โ„๏ธ"]
        elif self.hero_class == "Rogue":
            self.abilities = ["Stealth ๐ŸŒ‘", "Backstab ๐Ÿ—ก๏ธ"]
    
    def special_attack(self, target):
        """๐ŸŒŸ Use special ability"""
        if self.mana >= 20:
            self.mana -= 20
            special_damage = self.damage * 2
            target.take_damage(special_damage)
            ability = self.abilities[0]
            return f"{self.name} uses {ability}! Deals {special_damage} damage! ๐Ÿ’ซ"
        return f"{self.name} is out of mana! ๐Ÿ˜ฐ"

# ๐Ÿ‘น Monster class
class Monster(GameCharacter):
    def __init__(self, name, monster_type, health, damage, loot):
        super().__init__(name, health, damage)
        self.monster_type = monster_type
        self.loot = loot
    
    def drop_loot(self):
        """๐Ÿ’ฐ Drop loot when defeated"""
        return f"{self.name} dropped: {self.loot}! ๐ŸŽ"
    
    def rage_mode(self):
        """๐Ÿ˜ˆ Enter rage mode when low health"""
        if self.current_health < self.max_health * 0.3:
            self.damage *= 1.5
            return f"{self.name} enters RAGE MODE! ๐Ÿ”ฅ"
        return None

# ๐Ÿ‰ Boss monster
class BossMonster(Monster):
    def __init__(self, name, health, damage, loot):
        super().__init__(name, "Boss", health, damage, loot)
        self.phase = 1
        self.special_abilities = ["Earthquake ๐ŸŒ", "Fire Breath ๐Ÿ”ฅ", "Summon Minions ๐Ÿ‘ฅ"]
    
    def phase_transition(self):
        """๐ŸŽญ Boss changes phases"""
        health_percentage = self.current_health / self.max_health
        if health_percentage <= 0.66 and self.phase == 1:
            self.phase = 2
            self.damage *= 1.3
            return f"{self.name} enters Phase 2! Power increased! โšก"
        elif health_percentage <= 0.33 and self.phase == 2:
            self.phase = 3
            self.damage *= 1.5
            return f"{self.name} enters FINAL PHASE! Maximum power! ๐ŸŒŸ"
        return None

# ๐ŸŽฎ Let's play!
hero = Hero("Aragorn", "Warrior")
dragon = BossMonster("Smaug the Terrible", 500, 40, "Legendary Sword ๐Ÿ—ก๏ธ + 1000 gold ๐Ÿ’ฐ")

print(f"โš”๏ธ {hero.name} the {hero.hero_class} encounters {dragon.name}!")
print(hero.attack(dragon))
print(dragon.phase_transition())  # Check for phase change
print(hero.special_attack(dragon))

๐Ÿš€ Advanced Concepts

๐Ÿง™โ€โ™‚๏ธ Advanced Topic 1: Method Resolution Order (MRO)

When youโ€™re ready to level up, understand how Python finds methods:

# ๐ŸŽฏ Understanding MRO with diamond inheritance
class A:
    def method(self):
        return "A's method ๐Ÿ…ฐ๏ธ"

class B(A):
    def method(self):
        return "B's method ๐Ÿ…ฑ๏ธ"

class C(A):
    def method(self):
        return "C's method ยฉ๏ธ"

class D(B, C):
    pass  # ๐Ÿค” Which method will D use?

# ๐Ÿ” Check the MRO
print(D.__mro__)  # Shows the order Python searches
# (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

d = D()
print(d.method())  # B's method ๐Ÿ…ฑ๏ธ (B comes before C in MRO)

# ๐Ÿช„ Using super() in complex hierarchies
class E(B, C):
    def method(self):
        # Call all parent methods
        results = []
        for parent in E.__bases__:
            results.append(parent.method(self))
        return f"E combines: {' + '.join(results)} โœจ"

๐Ÿ—๏ธ Advanced Topic 2: Mixins and Composition

For the brave developers:

# ๐Ÿš€ Mixin classes add specific functionality
class TimestampMixin:
    """โฐ Add timestamp functionality"""
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        from datetime import datetime
        self.created_at = datetime.now()
        self.updated_at = datetime.now()
    
    def touch(self):
        """๐Ÿ”„ Update timestamp"""
        from datetime import datetime
        self.updated_at = datetime.now()
        return f"Updated at {self.updated_at.strftime('%Y-%m-%d %H:%M:%S')} ๐Ÿ“…"

class SerializableMixin:
    """๐Ÿ’พ Add JSON serialization"""
    def to_dict(self):
        """Convert to dictionary"""
        return {
            key: value for key, value in self.__dict__.items()
            if not key.startswith('_')
        }
    
    def to_json(self):
        """Convert to JSON string"""
        import json
        return json.dumps(self.to_dict(), default=str)

# ๐ŸŽจ Combine mixins with inheritance
class User(TimestampMixin, SerializableMixin):
    def __init__(self, username, email):
        super().__init__()
        self.username = username
        self.email = email
        self.posts = []
    
    def add_post(self, content):
        """โœ๏ธ Add a new post"""
        self.posts.append({
            'content': content,
            'timestamp': self.updated_at
        })
        self.touch()
        return f"Posted: {content} ๐Ÿ“"

# ๐ŸŽฎ Use the enhanced class
user = User("pythonista", "[email protected]")
print(user.add_post("Inheritance is awesome! ๐Ÿš€"))
print(user.to_json())  # Automatic JSON serialization!

โš ๏ธ Common Pitfalls and Solutions

๐Ÿ˜ฑ Pitfall 1: Forgetting to Call super()

# โŒ Wrong way - parent __init__ not called!
class Bird(Animal):
    def __init__(self, name, age, wingspan):
        # Oops! Forgot super().__init__()
        self.wingspan = wingspan  # ๐Ÿ’ฅ name and age not set!

# โœ… Correct way - always call super()!
class Bird(Animal):
    def __init__(self, name, age, wingspan):
        super().__init__(name, age)  # ๐Ÿ‘ Parent attributes initialized
        self.wingspan = wingspan

๐Ÿคฏ Pitfall 2: Circular Inheritance

# โŒ Dangerous - circular inheritance!
# class A(B):  # A inherits from B
#     pass
# 
# class B(A):  # B inherits from A - ๐Ÿ’ฅ Error!
#     pass

# โœ… Safe - proper hierarchy!
class Vehicle:
    """๐Ÿš— Base vehicle class"""
    pass

class Car(Vehicle):
    """๐Ÿš™ Cars are vehicles"""
    pass

class ElectricCar(Car):
    """๐Ÿ”‹ Electric cars are cars"""
    pass

๐Ÿ˜ฐ Pitfall 3: Overriding Without Understanding

# โŒ Wrong - completely replacing parent behavior
class Cat(Animal):
    def __init__(self, name):
        self.name = name
        # Lost age attribute! ๐Ÿ˜ฑ

# โœ… Correct - extend parent behavior
class Cat(Animal):
    def __init__(self, name, age, favorite_toy):
        super().__init__(name, age)  # Keep parent behavior
        self.favorite_toy = favorite_toy  # Add new behavior

๐Ÿ› ๏ธ Best Practices

  1. ๐ŸŽฏ Keep It Simple: Donโ€™t create deep inheritance hierarchies (max 3-4 levels)
  2. ๐Ÿ“ Use Descriptive Names: ElectricCar not EC
  3. ๐Ÿ›ก๏ธ Favor Composition: Sometimes combining objects is better than inheritance
  4. ๐ŸŽจ Single Responsibility: Each class should do one thing well
  5. โœจ Document Your Classes: Use docstrings to explain purpose and usage

๐Ÿงช Hands-On Exercise

๐ŸŽฏ Challenge: Build a School Management System

Create a school management system with inheritance:

๐Ÿ“‹ Requirements:

  • โœ… Base Person class with name, age, and ID
  • ๐ŸŽ“ Student class with grades and courses
  • ๐Ÿ‘จโ€๐Ÿซ Teacher class with subjects and salary
  • ๐Ÿซ Principal class with school management methods
  • ๐Ÿ“š Course enrollment system
  • ๐ŸŽจ Each person type needs unique abilities!

๐Ÿš€ Bonus Points:

  • Add grade calculation methods
  • Implement course prerequisites
  • Create a report card generator

๐Ÿ’ก Solution

๐Ÿ” Click to see solution
# ๐ŸŽฏ School Management System
from datetime import datetime
import random

class Person:
    """๐Ÿ‘ค Base class for all people in school"""
    def __init__(self, name, age, person_id):
        self.name = name
        self.age = age
        self.person_id = person_id
        self.email = f"{name.lower().replace(' ', '.')}@school.edu"
    
    def introduce(self):
        """๐Ÿ‘‹ Introduce yourself"""
        return f"Hi! I'm {self.name}, {self.age} years old! ๐Ÿ˜Š"
    
    def birthday(self):
        """๐ŸŽ‚ Celebrate birthday"""
        self.age += 1
        return f"Happy birthday {self.name}! Now {self.age} years old! ๐ŸŽ‰"

class Student(Person):
    """๐ŸŽ“ Student class"""
    def __init__(self, name, age, student_id, grade_level):
        super().__init__(name, age, student_id)
        self.grade_level = grade_level
        self.courses = {}  # course_name: grade
        self.gpa = 0.0
    
    def enroll_course(self, course_name):
        """๐Ÿ“š Enroll in a course"""
        if course_name not in self.courses:
            self.courses[course_name] = None
            return f"{self.name} enrolled in {course_name}! ๐Ÿ“–"
        return f"{self.name} is already enrolled in {course_name}! ๐Ÿค”"
    
    def receive_grade(self, course_name, grade):
        """๐Ÿ“ Receive a grade"""
        if course_name in self.courses:
            self.courses[course_name] = grade
            self.calculate_gpa()
            return f"{self.name} received {grade} in {course_name}! {'๐ŸŒŸ' if grade >= 90 else 'โœ…'}"
        return f"{self.name} is not enrolled in {course_name}! โŒ"
    
    def calculate_gpa(self):
        """๐ŸŽฏ Calculate GPA"""
        graded_courses = [g for g in self.courses.values() if g is not None]
        if graded_courses:
            self.gpa = sum(graded_courses) / len(graded_courses)
        return self.gpa
    
    def get_report_card(self):
        """๐Ÿ“‹ Generate report card"""
        report = f"\n๐ŸŽ“ Report Card for {self.name}\n"
        report += f"{'='*30}\n"
        for course, grade in self.courses.items():
            if grade is not None:
                emoji = '๐ŸŒŸ' if grade >= 90 else 'โœ…' if grade >= 70 else 'โš ๏ธ'
                report += f"{course}: {grade}% {emoji}\n"
        report += f"{'='*30}\n"
        report += f"GPA: {self.gpa:.2f} {'๐Ÿ†' if self.gpa >= 90 else '๐Ÿ“Š'}\n"
        return report

class Teacher(Person):
    """๐Ÿ‘จโ€๐Ÿซ Teacher class"""
    def __init__(self, name, age, teacher_id, subjects, salary):
        super().__init__(name, age, teacher_id)
        self.subjects = subjects
        self.salary = salary
        self.students = []
        self.classes = {}
    
    def assign_grade(self, student, course, grade):
        """๐Ÿ“ Assign grade to student"""
        if isinstance(student, Student):
            result = student.receive_grade(course, grade)
            return f"Teacher {self.name}: {result}"
        return "Can only grade students! ๐Ÿคท"
    
    def teach_class(self, course_name):
        """๐Ÿซ Teach a class"""
        if course_name in self.subjects:
            return f"{self.name} is teaching {course_name}! ๐Ÿ“š"
        return f"{self.name} doesn't teach {course_name}! โŒ"
    
    def get_raise(self, percentage):
        """๐Ÿ’ฐ Get a salary raise"""
        raise_amount = self.salary * (percentage / 100)
        self.salary += raise_amount
        return f"{self.name} got a {percentage}% raise! New salary: ${self.salary:,.2f} ๐ŸŽ‰"

class Principal(Teacher):
    """๐Ÿซ Principal class"""
    def __init__(self, name, age, principal_id, salary, school_name):
        super().__init__(name, age, principal_id, ["Administration"], salary)
        self.school_name = school_name
        self.staff = []
        self.students = []
        self.budget = 1000000  # $1M budget
    
    def hire_teacher(self, teacher):
        """๐Ÿ‘” Hire a new teacher"""
        if isinstance(teacher, Teacher) and self.budget >= teacher.salary:
            self.staff.append(teacher)
            self.budget -= teacher.salary
            return f"Welcome to {self.school_name}, {teacher.name}! ๐Ÿค"
        return "Cannot hire teacher - budget constraints! ๐Ÿ’ธ"
    
    def enroll_student(self, student):
        """๐ŸŽ“ Enroll a new student"""
        if isinstance(student, Student):
            self.students.append(student)
            return f"Welcome to {self.school_name}, {student.name}! ๐ŸŽ‰"
        return "Can only enroll students! ๐Ÿค”"
    
    def make_announcement(self, message):
        """๐Ÿ“ข Make school-wide announcement"""
        return f"๐Ÿ“ข Principal {self.name} announces: {message}"
    
    def school_report(self):
        """๐Ÿ“Š Generate school report"""
        avg_gpa = sum(s.gpa for s in self.students) / len(self.students) if self.students else 0
        return f"""
        ๐Ÿซ {self.school_name} Report
        {'='*30}
        ๐Ÿ‘จโ€๐Ÿซ Teachers: {len(self.staff)}
        ๐ŸŽ“ Students: {len(self.students)}
        ๐Ÿ“Š Average GPA: {avg_gpa:.2f}
        ๐Ÿ’ฐ Budget: ${self.budget:,.2f}
        """

# ๐ŸŽฎ Let's run our school!
# Create principal
principal = Principal("Dr. Smith", 55, "P001", 120000, "Python Academy")

# Create teachers
math_teacher = Teacher("Ms. Johnson", 35, "T001", ["Math", "Statistics"], 65000)
cs_teacher = Teacher("Mr. Lee", 40, "T002", ["Computer Science", "Python"], 70000)

# Hire teachers
print(principal.hire_teacher(math_teacher))
print(principal.hire_teacher(cs_teacher))

# Create students
alice = Student("Alice Brown", 16, "S001", 11)
bob = Student("Bob Wilson", 17, "S002", 12)

# Enroll students
print(principal.enroll_student(alice))
print(principal.enroll_student(bob))

# Students enroll in courses
alice.enroll_course("Math")
alice.enroll_course("Computer Science")
bob.enroll_course("Math")
bob.enroll_course("Python")

# Teachers assign grades
print(math_teacher.assign_grade(alice, "Math", 95))
print(cs_teacher.assign_grade(alice, "Computer Science", 88))
print(math_teacher.assign_grade(bob, "Math", 82))
print(cs_teacher.assign_grade(bob, "Python", 91))

# Show report cards
print(alice.get_report_card())
print(bob.get_report_card())

# Principal makes announcement
print(principal.make_announcement("Congratulations to all our honor roll students! ๐Ÿ†"))

# Show school report
print(principal.school_report())

๐ŸŽ“ Key Takeaways

Youโ€™ve learned so much! Hereโ€™s what you can now do:

  • โœ… Create inheritance hierarchies with confidence ๐Ÿ’ช
  • โœ… Use super() correctly to call parent methods ๐Ÿ›ก๏ธ
  • โœ… Override methods to customize behavior ๐ŸŽฏ
  • โœ… Avoid common inheritance pitfalls ๐Ÿ›
  • โœ… Build real-world applications using OOP principles! ๐Ÿš€

Remember: Inheritance is like building with LEGO blocks - start with a solid foundation and add pieces as needed! ๐Ÿงฑ

๐Ÿค Next Steps

Congratulations! ๐ŸŽ‰ Youโ€™ve mastered creating subclasses with inheritance!

Hereโ€™s what to do next:

  1. ๐Ÿ’ป Practice with the school management exercise above
  2. ๐Ÿ—๏ธ Build a small project using inheritance (maybe a zoo management system? ๐Ÿฆ)
  3. ๐Ÿ“š Move on to our next tutorial: Method Overriding and Polymorphism
  4. ๐ŸŒŸ Share your inheritance creations with the Python community!

Remember: Every Python expert started exactly where you are. Keep coding, keep learning, and most importantly, have fun! ๐Ÿš€


Happy coding! ๐ŸŽ‰๐Ÿš€โœจ