Object-Oriented Design

object-oriented-python
Choose composition, inheritance, or simpler functions based on the relationship you need.
  • Level: Intermediate
  • Estimated time: 35–50 minutes
  • You will learn: Choose composition, inheritance, or simpler functions based on the relationship you need.
  • Practice in: VS Code, Jupyter, or Colab

Questions

  • What problem does Object-Oriented Design help us solve in a small Python program?
  • What should we predict before running the example?
  • What value, output, or error should we inspect after changing one line?

Objectives

  • Run a complete example for object-oriented design in Colab.
  • Explain the example line by line using plain language.
  • Change one part of the code and predict the result before running it.
  • Recognize one common mistake and use the error message as evidence.

Hands-on episode: Object-Oriented Design

Use functions for actions, dictionaries or dataclasses for records, composition for objects made of other objects, and inheritance only when a genuine is-a relationship helps.

We will learn this by running code, not by memorizing a definition first. Open the Colab notebook from the button above, find this section, and run each cell in order. Keep a small note beside the notebook with three columns: prediction, actual result, and what changed.

Example 1.1

Read this as composition: a report uses a formatter instead of becoming every possible formatter.

class TextFormatter:
def format(self, title, value):
return f"{title}: {value}"

class Report:
def __init__(self, formatter):
self.formatter = formatter

def line(self, title, value):
return self.formatter.format(title, value)

Run the cell once without editing it. If the result is different from your prediction, leave the prediction visible and write one sentence about the difference. That sentence is more useful than a perfect first guess.

Explain Example 1.1

  • TextFormatter has one job: turn a title and value into text.
  • Report receives a formatter object instead of hard-coding one formatting style.
  • The report delegates formatting to the object it contains, which is composition.

Now explain the example out loud or in a Markdown cell. Use short sentences: “this line creates…”, “this name stores…”, “this output appears because…”. If you cannot explain a line yet, run only the lines above it and inspect the values that exist at that moment.

Challenge 1.1

NoteChallenge

Replace TextFormatter with a MarkdownFormatter that returns **title:** value. The report class does not need to change because the collaborator has the same method.

Show a safe way to approach the challenge
  1. Copy Example 1.1 into a new Colab cell.
  2. Change exactly one value, name, condition, or line.
  3. Write the expected output before running the cell.
  4. Run the cell and compare the actual result with your prediction.
  5. If the result surprises you, undo the change and try a smaller one.

Suggested first move: Replace TextFormatter with a MarkdownFormatter that returns **title:** value.

Debugging checkpoint 1.1

WarningDebugging checkpoint

Avoid deep inheritance trees for beginner projects. If you cannot explain the relationship as a clear ‘is a’ sentence, composition or a plain function is probably easier. A class that only wraps one unrelated function may be unnecessary.

Do not debug by rewriting the whole example. Read the error type or surprising output, inspect the closest value with print(...) or type(...), then change one thing. This is the same routine you will use in larger projects.

Apply it

Design a tiny project two ways: once with functions and dictionaries, once with classes. Write which version a beginner teammate would understand faster and why.

Finish by adding a Markdown cell that answers: What did this example teach me that I can reuse in a project?

Key points

  • Learn the concept by running a complete, small example first.
  • Predict before execution so your thinking becomes visible.
  • Change one thing at a time so cause and effect stay clear.
  • Treat errors as clues about the exact line or value Python could not handle.

Why this matters

Choose composition, inheritance, or simpler functions based on the relationship you need.

This lesson combines related subtopics that belong together in one learning conversation. You will still pause for a quiz after each section, but you do not need to jump between separate pages while building one clear explanation.

NoteGuiding questions

By the end of this lesson, you should be able to answer:

  • How do the sections in Object-Oriented Design fit together?
  • Which small example demonstrates each section?
  • Which debugging clue should I check first for each section?
NoteLearning objectives

You will practice how to:

  • explain the shared concept for this lesson;
  • use each section as one step in a larger workflow;
  • complete 3 short section quizzes before moving on;
  • connect examples, mistakes, and debugging routines.

Lesson map

  • 1. Composition — Build objects out of other objects.
  • 2. Inheritance — Reuse and specialize behavior with parent and child classes.
  • 3. When Not to Use Classes — Choose simple functions or data structures when classes add no clarity.

How objects can collaborate

Composition and inheritance answer different design questions: does one object have another object, or is it a specialized kind of another object?

classDiagram
  class Car
  class Engine
  Car *-- Engine : has-a
  class Animal
  class Cat
  Animal <|-- Cat : is-a

Prefer the relationship that makes the program easiest to explain and test.

1. Composition

Build objects out of other objects.

TipAnalogy

Composition is a bicycle made of wheels, frame, chain, and brakes: each part has a role inside the whole.

What this means

Composition models has-a relationships by placing one object inside another.

Example 1

Predict what will happen before you run the code.

class Engine:
    def start(self):
        print("engine on")

class Car:
    def __init__(self):
        self.engine = Engine()

car = Car()
car.engine.start()

Step-by-step explanation

  1. class Engine: — pause here and say what this line reads, creates, changes, or displays.
  2. def start(self): — pause here and say what this line reads, creates, changes, or displays.
  3. print("engine on") — pause here and say what this line reads, creates, changes, or displays.
  4. class Car: — pause here and say what this line reads, creates, changes, or displays.
  5. def __init__(self): — pause here and say what this line reads, creates, changes, or displays.
  6. Continue the same process for the remaining lines, one line at a time.

After running the example, compare the actual output with your prediction. If they differ, do not erase your prediction. The difference is the part that can teach you the most.

Challenge

NotePractice

Change one input value, predict the new output, run the code, and explain the difference in one sentence.

Show one possible solution path
  1. Copy Example 1 into Colab, Jupyter, or a .py file.
  2. Mark the line you plan to change.
  3. Write a one-sentence prediction.
  4. Run the changed code.
  5. If the result surprises you, restore the original and change a smaller part.

The goal is not to find the only correct answer. The goal is to create a small experiment where you can explain cause and effect.

Common mistakes

WarningCommon mistake

If inheritance feels forced, composition is often simpler and clearer.

When you get stuck, use this debugging routine:

  1. Read the last line of the error message or inspect the unexpected output.
  2. Find the smallest line of code that could be responsible.
  3. Print or inspect the value and type at that point.
  4. Change one thing.
  5. Run again and record what changed.

Check your understanding

This quiz checks the ideas in this section before you move on.

2. Inheritance

Reuse and specialize behavior with parent and child classes.

TipAnalogy

Inheritance is like a school form: all students share common fields, while exchange students may add extra fields.

What this means

Inheritance models is-a relationships where a child class specializes a parent class.

Example 2

Predict what will happen before you run the code.

class Animal:
    def speak(self):
        print("sound")

class Cat(Animal):
    def speak(self):
        print("meow")

Cat().speak()

Step-by-step explanation

  1. class Animal: — pause here and say what this line reads, creates, changes, or displays.
  2. def speak(self): — pause here and say what this line reads, creates, changes, or displays.
  3. print("sound") — pause here and say what this line reads, creates, changes, or displays.
  4. class Cat(Animal): — pause here and say what this line reads, creates, changes, or displays.
  5. def speak(self): — pause here and say what this line reads, creates, changes, or displays.
  6. Continue the same process for the remaining lines, one line at a time.

After running the example, compare the actual output with your prediction. If they differ, do not erase your prediction. The difference is the part that can teach you the most.

Challenge

NotePractice

Change one input value, predict the new output, run the code, and explain the difference in one sentence.

Show one possible solution path
  1. Copy Example 1 into Colab, Jupyter, or a .py file.
  2. Mark the line you plan to change.
  3. Write a one-sentence prediction.
  4. Run the changed code.
  5. If the result surprises you, restore the original and change a smaller part.

The goal is not to find the only correct answer. The goal is to create a small experiment where you can explain cause and effect.

Common mistakes

WarningCommon mistake

Do not use inheritance only to share two lines of code. It should express a clear relationship.

When you get stuck, use this debugging routine:

  1. Read the last line of the error message or inspect the unexpected output.
  2. Find the smallest line of code that could be responsible.
  3. Print or inspect the value and type at that point.
  4. Change one thing.
  5. Run again and record what changed.

Check your understanding

This quiz checks the ideas in this section before you move on.

3. When Not to Use Classes

Choose simple functions or data structures when classes add no clarity.

TipAnalogy

A class can be a backpack, but you do not need a backpack to carry one sticky note.

What this means

Classes are useful when state and behavior stay together; otherwise they can hide simple ideas.

Example 3

Predict what will happen before you run the code.

def calculate_total(prices):
    return sum(prices)

print(calculate_total([3, 4, 5]))

Step-by-step explanation

  1. def calculate_total(prices): — pause here and say what this line reads, creates, changes, or displays.
  2. return sum(prices) — pause here and say what this line reads, creates, changes, or displays.
  3. print(calculate_total([3, 4, 5])) — pause here and say what this line reads, creates, changes, or displays.

After running the example, compare the actual output with your prediction. If they differ, do not erase your prediction. The difference is the part that can teach you the most.

Challenge

NotePractice

Change one input value, predict the new output, run the code, and explain the difference in one sentence.

Show one possible solution path
  1. Copy Example 1 into Colab, Jupyter, or a .py file.
  2. Mark the line you plan to change.
  3. Write a one-sentence prediction.
  4. Run the changed code.
  5. If the result surprises you, restore the original and change a smaller part.

The goal is not to find the only correct answer. The goal is to create a small experiment where you can explain cause and effect.

Common mistakes

WarningCommon mistake

Creating a class for every noun can make beginner code harder to read. Start simple, then refactor when patterns appear.

When you get stuck, use this debugging routine:

  1. Read the last line of the error message or inspect the unexpected output.
  2. Find the smallest line of code that could be responsible.
  3. Print or inspect the value and type at that point.
  4. Change one thing.
  5. Run again and record what changed.

Check your understanding

This quiz checks the ideas in this section before you move on.

Notebook and Colab practice

Open a blank notebook at https://colab.new. Use one section at a time: copy the Example 1, predict the result, run it, answer the section quiz, and then move to the next section. This is better than copying the entire page at once.

Instructor note

Teaching notes
  • Treat each section as a short teaching episode.
  • Pause for the section quiz before introducing the next section.
  • Ask learners to compare sections: what stayed the same, and what changed?
  • If time is short, teach the first two sections live and assign the rest as practice.

Key points

TipKey points
  • Composition: Composition models has-a relationships by placing one object inside another.
  • Inheritance: Inheritance models is-a relationships where a child class specializes a parent class.
  • When Not to Use Classes: Classes are useful when state and behavior stay together; otherwise they can hide simple ideas.
  • Use the section quizzes as gates: review before moving on if a quiz feels uncertain.

References

  • Python classes tutorial: https://docs.python.org/3/tutorial/classes.html
  • dataclasses documentation: https://docs.python.org/3/library/dataclasses.html
  • Python Tutorial: https://docs.python.org/3/tutorial/
  • Quarto OJS documentation: https://quarto.org/docs/interactive/ojs/
  • ipywidgets documentation: https://ipywidgets.readthedocs.io/en/stable/
Back to top