Fatskills
Practice. Master. Repeat.
Study Guide: Grade 11 Computer Science Study Guide: Python – Advanced OOP and Inheritance
Source: https://www.fatskills.com/grade-11/chapter/grade-11-computer-science-study-guide-python-advanced-oop-and-inheritance

Grade 11 Computer Science Study Guide: Python – Advanced OOP and Inheritance

By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.

⏱️ ~9 min read

Grade 11 Computer Science Study Guide: Python – Advanced OOP and Inheritance


1. The Driving Question

"If I’m building a game where a Dragon and a Knight both attack, but the Dragon breathes fire and the Knight swings a sword, how do I write the code so I don’t have to rewrite the same ‘attack’ logic twice? And why does Python let me change how a method works in a child class without breaking the parent’s version?"

By the end of this guide, you’ll see inheritance not just as a way to reuse code, but as a tool for modeling real-world relationships—where some behaviors are shared, some are unique, and some are modified in specific ways.


2. The Core Idea – Built, Not Listed

Imagine you’re designing a tabletop RPG (like Dungeons & Dragons). Every character—whether a Rogue, a Mage, or a Paladin—has a name, health points, and an inventory. But each class also has unique abilities: the Mage casts spells, the Rogue picks locks, and the Paladin heals allies.

Instead of writing three separate classes with duplicate code for name and health, you create a parent class (Character) that holds the shared attributes and methods. Then, each child class (Rogue, Mage, Paladin) inherits those basics but extends or overrides them with their own special behaviors. This is inheritance: a way to organize code hierarchically, just like how a Dragon is a type of Monster, which is a type of Character.

Python enforces this with the super() function, which lets child classes call the parent’s version of a method (e.g., super().attack()) while still adding their own twist (e.g., the Dragon’s fire breath). This isn’t just about saving lines of code—it’s about modeling relationships where some behaviors are shared, some are unique, and some are customized.

Key Vocabulary

  1. Inheritance
  2. Definition: A mechanism where a child class (subclass) automatically gains the attributes and methods of a parent class (superclass), while still being able to add or modify them.
  3. Example: In a music streaming app, a Song class might have title and duration, but a Podcast subclass could add host and episode_number while keeping the same play() method.
  4. College Note: In languages like Java or C++, inheritance is stricter (e.g., final classes/methods can’t be overridden). Python’s dynamic typing makes inheritance more flexible but also riskier—college courses often emphasize composition over inheritance to avoid deep hierarchies.

  5. Method Overriding

  6. Definition: When a child class provides its own implementation of a method that’s already defined in its parent class.
  7. Example: A BankAccount class has a withdraw() method that checks for sufficient funds. A SavingsAccount subclass might override this to also enforce a monthly withdrawal limit.
  8. College Note: In functional programming (e.g., Haskell), overriding is rare because functions are pure and immutable. Python’s OOP blends paradigms, so overriding is common but requires careful design.

  9. super()

  10. Definition: A function that lets a child class call a method from its parent class, often used to extend (not replace) the parent’s behavior.
  11. Example: In a fitness app, a Workout class might track duration and calories_burned. A RunningWorkout subclass could use super().end_workout() to log the session and then add a distance_km field.
  12. College Note: In multiple inheritance (e.g., a class inheriting from two parents), super() follows the Method Resolution Order (MRO), which can get complex. Python’s mro() method helps debug this.

  13. Abstract Base Class (ABC)

  14. Definition: A class that cannot be instantiated and is meant to be inherited from, often defining methods that must be implemented by child classes.
  15. Example: In a payment processing system, an abstract PaymentMethod class might require subclasses like CreditCard and PayPal to implement process_payment().
  16. College Note: In statically typed languages (e.g., Java), abstract classes are enforced at compile time. Python’s abc module enforces them at runtime, which can lead to subtle bugs if not used carefully.

3. Assessment Translation

How This Appears on Assessments

  • Classroom/AP CSA (Computer Science A): Free-response questions (FRQs) where you must:
  • Define a parent and child class with inheritance.
  • Override a method in the child class.
  • Use super() to extend parent behavior.
  • Write a method that leverages polymorphism (e.g., a function that takes a Character and calls its attack() method, which behaves differently for Dragon vs. Knight).
  • SAT/ACT (if applicable): Rare, but may appear in the Digital SAT’s "Problem Solving and Data Analysis" section as a logic puzzle about class relationships.
  • State Standards (e.g., CSTA, NGSS for CS): Focus on:
  • Designing class hierarchies to model real-world systems.
  • Explaining how inheritance reduces code duplication.
  • Debugging inheritance-related errors (e.g., AttributeError when a child class misses an overridden method).

What a "Proficient" Response Looks Like

Prompt: "Define a Vehicle class with a move() method that prints 'Moving...'. Then, create a Car subclass that overrides move() to print 'Driving at X mph', where X is a speed passed to the constructor. Use super() to avoid rewriting the parent’s move() logic."

Proficient Student Response:

class Vehicle:
    def move(self):
        print("Moving...")

class Car(Vehicle):
    def __init__(self, speed):
        self.speed = speed

    def move(self):
        super().move()  # Calls Vehicle's move() first
        print(f"Driving at {self.speed} mph")

# Test
my_car = Car(60)
my_car.move()

Output:

Moving...
Driving at 60 mph

What the Teacher Looks For: - Correct use of super() to extend (not replace) the parent’s method. - Proper initialization of speed in the child class. - No redundant code (e.g., not rewriting print("Moving...") in Car). - AP CSA Rubric: Full credit for: - Correct inheritance syntax (class Car(Vehicle)). - Proper method overriding with super(). - Working test case.

Distractor Patterns in Multiple Choice: - Wrong Parentheses: super.move() instead of super().move(). - Missing self: Forgetting self in the child’s __init__. - Overriding vs. Extending: Replacing the parent’s method entirely instead of using super(). - Instantiating ABCs: Trying to create an object from an abstract class (e.g., Vehicle() when Vehicle is an ABC).


4. Mistake Taxonomy

Mistake 1: Forgetting super() in __init__

Prompt: "Define a Person class with name and age attributes. Then create a Student subclass that adds a grade attribute. Initialize all three attributes in Student."

Common Wrong Response:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

class Student(Person):
    def __init__(self, name, age, grade):
        self.grade = grade  # Forgot to call Person.__init__!

Why It Loses Credit: - The Student class doesn’t initialize name and age, so student.name and student.age will raise AttributeError. - Assessment Format: On an FRQ, this would lose points for incomplete initialization and not leveraging inheritance.

Correct Approach:

class Student(Person):
    def __init__(self, name, age, grade):
        super().__init__(name, age)  # Initialize parent attributes
        self.grade = grade

Mistake 2: Overriding Without super() (Breaking Parent Behavior)

Prompt: "A BankAccount class has a withdraw(amount) method that checks if amount <= balance. Override this in a SavingsAccount subclass to also enforce a max_withdrawal limit (e.g., $500/month)."

Common Wrong Response:

class SavingsAccount(BankAccount):
    def withdraw(self, amount):
        if amount > self.max_withdrawal:  # Forgot to check balance!
            print("Monthly limit exceeded!")
            return False
        self.balance -= amount
        return True

Why It Loses Credit: - The child class replaces the parent’s logic instead of extending it. If amount > balance, the withdrawal will still go through. - Assessment Format: On an AP FRQ, this would lose points for not preserving parent behavior and logical error.

Correct Approach:

class SavingsAccount(BankAccount):
    def __init__(self, balance, max_withdrawal):
        super().__init__(balance)
        self.max_withdrawal = max_withdrawal

    def withdraw(self, amount):
        if amount > self.max_withdrawal:
            print("Monthly limit exceeded!")
            return False
        return super().withdraw(amount)  # Reuse parent's balance check

Mistake 3: Misusing Abstract Base Classes (ABCs)

Prompt: "Define an abstract Shape class with an abstract area() method. Then create a Circle subclass that implements it."

Common Wrong Response:

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius
    # Forgot to implement area()!

Why It Loses Credit: - Circle doesn’t implement area(), so Circle(5) will raise TypeError: Can't instantiate abstract class Circle with abstract method area. - Assessment Format: On a state test, this would lose points for not fulfilling the ABC contract. On an AP FRQ, it’s an automatic zero for the subclass.

Correct Approach:

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14 * self.radius  2

5. Connection Layer

  1. Within CS-Design Patterns (Factory Method)
  2. Why it matters: Inheritance is the backbone of the Factory Method pattern, where a parent class defines a method (e.g., create_product()) that child classes override to return different objects (e.g., create_button() in a GUI framework). Understanding inheritance helps you see why this pattern exists—to decouple object creation from usage.

  3. Across Subjects-Biology (Taxonomy)

  4. Why it matters: Biological classification (e.g., Animal-Mammal-Dog) is a hierarchy just like OOP inheritance. A Dog inherits traits from Mammal (e.g., warm-blooded) but overrides others (e.g., bark() instead of meow()). This is the same logic as class Dog(Animal).

  5. Outside School-Game Modding (Minecraft, Roblox)

  6. Why it matters: In games like Minecraft, modders use inheritance to create custom mobs. A Creeper is a subclass of Monster, which is a subclass of Entity. Modders override methods like onDeath() to drop custom items. If you’ve ever wondered how mods add new behaviors without breaking the game, inheritance is the answer.

6. The Stretch Question

"In Python, you can dynamically add methods to a class at runtime (e.g., Dog.bark = lambda self: print('Woof!')). Does this break the idea of inheritance? Why or why not—and when would you actually use this in real code?"

Pointer Toward the Answer: This doesn’t break inheritance because inheritance is about relationships between classes, not just static code. Dynamic method addition is more like monkey-patching—useful for: - Testing: Temporarily replacing a method to isolate a bug (e.g., mocking requests.get in unit tests). - Plugins: Frameworks like Django use this to let users extend core classes without modifying the source code. - Metaprogramming: Libraries like SQLAlchemy use it to generate database methods on the fly.

The key difference? Inheritance is explicit (you declare class Dog(Animal)), while dynamic methods are implicit (you modify the class after it’s defined). Both have their place, but inheritance is better for designing systems, while dynamic methods are better for adapting them.