Skip to content

6 min read

American Corners

Abstract data types

What it does versus how it does it

When you use a Python list, you already think about it in two separate ways without noticing. There is what it lets you do: add an item, read the item at some position, ask its length. And there is how it manages that behind the scenes: where the values sit in memory, how it grows when it fills up. Most days you care only about the first. You call .append() and trust it to work; you never inspect the machinery.

That split has a name. An abstract data type, or ADT, is a description of a data type purely in terms of what operations it offers and how they behave, with no mention of how those operations are built. It is a contract. It says "you can do these things, and here is what each one promises", and stays silent on the internals.

The contract and the implementation

It helps to hold two words apart:

  • The abstract data type is the contract: the list of operations and the rules they follow. For a simple counter, the contract might be "start at zero, increase adds one, value reports the current total".
  • The implementation is the actual code that keeps the contract. There could be several different implementations of the very same contract, each with its own trade-offs. Code that uses only the promised operations cannot tell which one it got. Code that reaches past them, or that cares how long an operation takes, very much can.

Think of a light switch. The contract is "flip up for on, flip down for off". Whether the wiring behind the wall is old or new, copper or something else, does not change how you use the switch. The switch is the abstract type; the wiring is the implementation.

Why the separation pays off

Keeping the contract apart from the implementation buys you two real advantages.

First, you can use a data type by learning only its contract. You do not need to read the source of Python's list to store your grades in one. The promised operations are enough.

Second, whoever builds the type can change the implementation freely, as long as the contract still holds. If they find a faster way to store the data, your code keeps working untouched, because it only ever depended on the promises, never on the machinery. Code that leans on the contract survives; code that reaches inside the machinery breaks the moment the machinery changes.

Tip

When you meet a new data structure, learn its contract first: what can I add, what can I take out, what can I ask, and in what order do things come back? The internal cleverness can wait. The contract is what you actually program against.

A worked example: a scoreboard as a contract

Say a game needs a scoreboard. Before writing a line of storage code, describe the contract: what should a scoreboard do?

  • record(team): give one point to a team.
  • points(team): report how many points a team has.

That is the whole abstract type. Only now do you choose an implementation, and a dictionary from the earlier lesson fits neatly:

def new_board():
    return {}

def record(board, team):
    board[team] = board.get(team, 0) + 1

def points(board, team):
    return board.get(team, 0)

game = new_board()
record(game, "red")
record(game, "red")
record(game, "blue")
print(points(game, "red"))    # 2
print(points(game, "green"))  # 0

The rest of your program calls record and points and never touches the dictionary directly. That is the discipline the abstract type gives you: if you later swapped the dictionary for some other store, every one of those calls would keep working unchanged, because they only ever relied on the contract. The next two lessons, on stacks and queues, are two more contracts of exactly this kind.

Try this now

The lesson named a counter contract: "start at zero, increase adds one, value reports the total". Implement it in the scoreboard's style, storing the count inside a dictionary: new_counter() returns {"count": 0}, increase(counter) adds one to it, and value(counter) returns it. Make one counter, increase it three times, and print the value. The rest of your code never touches the number directly, only the contract.

Put both modules to work

The claim above is worth testing rather than believing: that swapping the store underneath would leave every call working. Here is the swap.

Unit practice · 25 to 30 min

Swap the implementation and see whose code survives

This lesson promises that if the dictionary underneath the scoreboard were swapped for something else, every call would keep working. That is worth testing rather than believing. Your work stays in this browser: nothing is uploaded, graded or kept.

The contract, exactly as the lesson states it

record(board, team)   ->  give one point to a team
points(board, team)   ->  report how many points a team has

Two operations. That is the whole abstract type. Nothing else is promised.

1 · Predict

A teammate wrote these four lines against the scoreboard. Today it is built on a dictionary. For each line, say whether it would still do its job if the implementation changed.

Ask how many points red has

print(points(game, "red"))

List every team on the board

for team in game:
    print(team)

Count how many teams are playing

print(len(game))

Give blue a point

record(game, "blue")
If you are stuck, read the contract literally

Go back to the two lines of the contract and read them as if a lawyer wrote them. They say a team can be given a point, and that a team's total can be reported. That is all they say.

Now take each line of code and ask one question: does this use only those two operations? If it does, it is safe. If it touches the board itself, with a loop, a length, an index or a key, it is reaching past the contract into the storage, and the storage is the part that is allowed to change.

That single test settles all four fragments without knowing anything about lists or dictionaries.

If that was straightforward

When is silence the right design? The obvious lesson from this practice is that contracts should promise more. That is half true and the other half is a trap.

Every promise you add is a freedom you take away from whoever implements it. Promise that teams come back in the order they scored, and you have quietly banned every future implementation that does not preserve order, including several that would be faster. Promise a constant-time lookup and you have banned the simple list version that is perfectly fine for eight teams. A contract that promises everything can only be implemented one way, at which point it has stopped being an abstract type at all.

This is why Python's own set promises no order even though any real set has some internal arrangement. Refusing to promise it keeps the implementation free, and it tells you honestly not to depend on what you might otherwise observe.

Your turn: take the clause you just wrote and argue the other side. What implementation does your promise now forbid, and is that a price worth paying for the program you actually have?

This device remembers only which stage you reached and which verdicts you gave.

Found something unclear, outdated or improvable? Suggest an improvement

Check yourself

  1. In your own words, what is the difference between an abstract data type and its implementation? Use the light-switch idea or an example of your own.
  2. Why can the author of a data type change how it stores data without breaking programs that use it, as long as one thing stays the same? What is that one thing?
  3. In the scoreboard example, the rest of the program only ever calls record and points. Why does that matter if you later decide to store the scores in a completely different way?

Where this lesson comes from

Built from

  • Data Structures and Algorithms
  • Further reading: Brian Heinold, A Practical Introduction to Python Programming

alphaPlan courses are built from taught programmes rather than invented for the web. Where a claim rests on an outside standard or a reported case, it is named above so you can check it rather than take our word for it.

This course was developed by alphaPlan Center from programs delivered in partnership with the American Corners network.

Found something unclear, outdated or improvable? Suggest an improvement