0%

Chapter 9 · practice

Inheritance and Polymorphism

Different Objects, the Same Operation

Every question in this course has been the same kind of question: a prompt checked against one stored text answer. Real quizzes are not like that.

  • A text question compares what was typed.

  • A multiple-choice question accepts one of several options by letter.

  • A numeric question accepts anything close enough to the right number.

Three different rules for deciding correctness. The interesting part is what the rest of the program should have to know about them.

The numeric examples use one small built-in helper. abs(number) returns the number’s distance from zero, so abs(-0.05) is 0.05. Comparing that distance with a tolerance tells us whether a numeric response is close enough.

The version that knows everything

Here is the obvious first attempt, and the reason this chapter exists:

Try it

It works. Now think about what happens next.

A fourth kind arrives, say true or false. You add a branch here. Then scoring needs to know the kind, so it grows the same chain. Then display, then the report, then whatever reads questions from storage.

The kind has spread through the program, and every place that reads it has to be found and updated together. Miss one and the new question type silently behaves like the last branch, or falls through to return False and is wrong for everybody.

The signal

The pattern to recognize is a chain of asking what kind of thing is this, followed by code doing the corresponding thing:

if kind == "text":
    ...
elif kind == "choice":
    ...
elif kind == "numeric":
    ...

One of these chains is fine. The problem is that they breed, and copies of the same chain in different drift apart. Adding a type means an archaeology expedition.

Compare it with a chain that is not this pattern:

if score >= 90:
    grade = "A"
elif score >= 80:
    grade = "B"

This asks about a , not about what kind of it is, and adding a grade band changes one place. Leave it alone. The pattern worth removing is the repeated branch on an object’s kind. This is separate from Chapter 7’s identity question of whether two names refer to the same object.

Which chain is the warning sign this chapter addresses?

The move

The way out is the idea Chapter 4 introduced and this chapter finishes: put the behavior on the object.

Try it

There is one deliberate rough edge: NumericQuestion.is_correct("three") raises . All three classes expose the same name, but they do not yet honor the same promise for ordinary input. Lesson 8 returns to that behavioral contract and repairs it.

The at the bottom is the whole point. It calls question.is_correct(response) three times, gets three different rules, and contains no at all. It never asks what kind of question it has.

Each object knows what it is, so nothing else has to. Adding a fourth type means writing one class that honors the same operations; consumers such as this loop keep working unchanged.

Using several different through one shared operation, without the calling code knowing which is which, is called polymorphism. The name is longer than the idea.

What this chapter is really about

You may have noticed those three classes were written without any of the machinery inheritance is usually introduced with. There is no base class, and nothing is shared.

That is deliberate. Polymorphism is the goal, and inheritance is one way to reach it, useful in narrower circumstances than most introductions suggest. The next two lessons show how far you can get with no inheritance at all, so that when it appears in Lesson 4 you can judge whether it is earning its place.

Task

This program decides everything by asking what kind of question it has, in two separate places that have already drifted: score_quiz handles three kinds, and describe_all handles only two.

The three validated question classes are supplied. Replace both chains with polymorphic calls.

Then rewrite score_quiz(questions, responses) and describe_all(questions) so neither contains an asking what kind of question it has. They should call the same two on every question and let each supply its own rule.

The point values stay as they are. Concentrate on the one new difficulty: consumer code that asks every object for the same operation without type tests.