Python Concepts Review: From Variables to APIs

Every tool you have met, sorted back into drawers

Advanced 15 min

In this lesson

You have covered a lot of ground. This lesson does not teach anything new: it sorts what you already know into drawers, so that when a problem turns up you can find the right tool instead of the one you used most recently.

Every entry below names the question the concept answers, because that is how you will actually recall it. Nobody thinks “I need a dictionary”; they think “I need to look something up by name”.

Explain it like I’m 5

You have filled a toolbox by picking up one tool at a time. This lesson lays them all out on the bench and labels the drawers.

The core language: five ideas everything else sits on

Units 1 to 3 gave you the whole foundation, and it really is only five ideas.

  • Values and variables — a name pointing at a value. Answers: how do I keep something to use later? (Variables)
  • Types — text, whole numbers, decimals, true/false. Answers: what kind of thing is this, and what can I do with it? (Data Types)
  • Control flowif to choose, for and while to repeat. Answers: how do I decide, and how do I do it again? (if Statements, for Loops)
  • Functions — a named block that takes input and returns a result. Answers: how do I stop writing this out three times? (Functions)
  • Files — reading and writing text on disk. Answers: how does anything survive the program ending? (Reading and Writing Files)

Almost every later topic is one of these five with a sharper edge on it. A generator is a function that pauses. A dataclass is a type you defined. A context manager is a try/finally with a name.

Example
# The whole foundation in nine lines.
temperatures = [18.2, 21.0, 19.4, 24.8, 22.1]

def describe(readings):
    """Return a one-line summary of some numbers."""
    average = sum(readings) / len(readings)
    verdict = "warm" if average > 20 else "cool"
    return f"{len(readings)} readings, average {average:.1f}, {verdict}"

print(describe(temperatures))
Output
5 readings, average 21.1, warm
Values, a type, control flow, a function, and a return value.

The four collections, and how to choose

This is the decision people get wrong most often, so it is worth a table rather than a paragraph. All four hold several things at once; they differ in what they are good at.

  • List [1, 2, 3] — ordered, changeable. The default. Use it when order matters or you keep appending.
  • Dictionary {"a": 1} — look a value up by a key. Use it whenever you catch yourself searching a list for a match.
  • Set {1, 2, 3} — no duplicates, unordered, very fast membership tests. Use it for de-duplicating and for “is this in the list of 50,000?”
  • Tuple (1, 2) — like a list, but cannot be changed. Use it for a fixed record such as a coordinate, or when you want to be sure nothing edits it.

The rule of thumb: reach for a dictionary sooner than feels natural. Scanning a list to find a match is the most common avoidable slowness in beginner code, and a dictionary replaces the whole loop with one lookup.

Example
people = [("ada", 36), ("sam", 41), ("rae", 29)]

# Searching a list: a loop, every time you need a name.
def age_of_slow(name):
    for person, age in people:
        if person == name:
            return age
    return None

# The same data as a dictionary: one lookup, no loop.
ages = dict(people)

print(age_of_slow("sam"), ages["sam"])
print(age_of_slow("kim"), ages.get("kim", "unknown"))
print("names:", sorted(set(ages)))
Output
41 41
None unknown
names: ['ada', 'rae', 'sam']
Same answer, but the second version stops being a loop.

Choose the right collection for each storage need. Replace each "?" with one of "list", "dict", "set", or "tuple". Ask yourself two questions each time: does the order matter, and am I looking things up by name?

NEEDS = {
    "the steps of a recipe, in order": "?",
    "look up a capital city by country name": "?",
    "strip duplicate email addresses out of a mailing list": "?",
    "an (x, y) point that must never be edited": "?",
    "a queue of jobs you keep appending to": "?",
    "check whether a word is in a 50,000-word banned list": "?",
}

for need, choice in NEEDS.items():
    print(f"{choice:6s} {need}")

Making a program hold up (Units 4, 9)

These are the habits that separate a script that worked once from one you can leave running.

  • try/exceptanswers: what should happen when this goes wrong? Catch the specific exception, not everything.
  • Logginganswers: what did the script do at 3am while I was asleep? A log is a journal; print() is a shout across the room.
  • Type hintsanswers: what is this function supposed to be given? Documentation a checker can verify.
  • Dataclassesanswers: how do I stop passing five loose variables around? A dataclass names the record.
  • Classesanswers: which data and which behavior belong together?
Example
from dataclasses import dataclass

@dataclass
class Reading:
    station: str
    celsius: float

def parse(line: str) -> Reading | None:
    """Turn 'oxford,18.2' into a Reading, or None if the line is unusable."""
    try:
        station, value = line.split(",")
        return Reading(station.strip(), float(value))
    except ValueError:
        return None

for line in ["oxford,18.2", "leeds,not-a-number", "york,21.0"]:
    print(line, "->", parse(line))
Output
oxford,18.2 -> Reading(station='oxford', celsius=18.2)
leeds,not-a-number -> None
york,21.0 -> Reading(station='york', celsius=21.0)
A dataclass, a type hint, and a deliberately narrow except, in ten lines.

The power tools, and what each one deletes

Unit 10 covered the features that exist to remove repetition. Each one is best remembered by the complaint it answers.

The matching warning from that unit still applies: plain, obvious code beats a clever abstraction used once. Wrap it when the same wrapper shows up a third time.

The domains: what you can actually build

The last five units were less about the language and more about what to point it at.

  • Automation (Unit 5) — rename, sort, back up, and tidy real files, with a dry-run mode first.
  • Web apps (Unit 6) — Flask routes, templates, and forms.
  • APIs and JSON (Unit 7) — call a service, page through the results, and flatten the response into clean records.
  • Databases and regex (Unit 8) — store what you fetched so it survives, and pull structure out of messy text.
  • Concurrency (Unit 11) — stop waiting on one thing at a time.
  • Data and models (Units 12-13) — pandas, charts, and a first prediction.
  • Games and GUIs (Unit 14) — event loops, and keeping logic apart from the screen.

One script, most of the toolbox

Concepts studied in isolation stay in isolation. Here is a single realistic function using seven of them at once, the shape of nearly every tool you will build from here.

Example
from dataclasses import dataclass

@dataclass
class Sale:
    region: str
    amount: float

RAW = [
    {"region": "north", "amount": "120.50"},
    {"region": "south", "amount": "89.00"},
    {"region": "north", "amount": "oops"},
    {"region": "south", "amount": "215.75"},
]

def clean(rows):
    """Turn messy records into Sale objects, skipping anything unusable."""
    for row in rows:
        try:
            yield Sale(row["region"], float(row["amount"]))
        except (KeyError, ValueError):
            print("  skipped:", row)

totals = {}
for sale in clean(RAW):
    totals[sale.region] = totals.get(sale.region, 0) + sale.amount

for region, total in sorted(totals.items()):
    print(f"{region}: {total:.2f}")
Output
  skipped: {'region': 'north', 'amount': 'oops'}
north: 120.50
south: 304.75
A dataclass, a generator, exception handling, a dictionary tally, sorting, and an f-string.

Common mistake: Treating forgotten syntax as proof you did not learn it

Why it happens:

Reading a lesson feels like knowing, so not recalling it later feels like losing something.

How to fix it:

You never had it memorized, and you do not need it. Aim to recognize which tool a problem calls for. Looking up the exact arguments is what documentation is for, and everyone does it.

Common mistake: Reviewing by re-reading instead of by building

Why it happens:

Re-reading is comfortable and feels productive.

How to fix it:

Close the page and write the thing from memory. The gaps you hit in five minutes of writing are worth an hour of re-reading, because they are the parts you actually do not know.

Common mistake: Scanning a list where a dictionary belongs

Why it happens:

Lists are the first collection you learn, so they become the default for everything.

How to fix it:

If you find yourself looping to find a matching item, you want a dictionary keyed on the thing you are matching. One lookup replaces the whole loop.

Common mistake: Skipping review straight after finishing a project

Why it happens:

The project works, so it feels finished.

How to fix it:

The half hour just after it works is when the lessons are sharpest. Write down what you had to look up — that list is your personal revision plan.

Which concept is for repeating work over many items?

You need to look a price up by product name. Which collection?

Which concept groups data together with the behavior that uses it?

What is the honest reason to reach for a generator?

Mini exercise (easy)

Ten real tasks, one concept each. For every task, name the Python concept you would reach for first. This is the recognition skill the whole lesson is about, and it is the one that survives when the syntax fades.

Practice here. Fill in the missing piece and click Run to try your answer in place.

# Name the concept each task needs. Use exactly one of:
#   "loop"  "dictionary"  "function"  "class"  "try/except"  "comprehension"
#   "generator"  "decorator"  "context manager"  "dataclass"
TASKS = {
    "do the same thing to every row in a file": "?",
    "look up a price by product name": "?",
    "reuse the same twenty lines in three places": "?",
    "keep a customer's details together with the code that uses them": "?",
    "carry on when one row in the file is malformed": "?",
    "build a list of every name in upper case, in one line": "?",
    "read a 4 GB log without loading it into memory": "?",
    "time six functions without editing any of them": "?",
    "guarantee the file is closed even if the code raises": "?",
    "hold a fixed set of fields with a free __init__ and __repr__": "?",
}

for task, concept in TASKS.items():
    print(f"{concept:16s} {task}")

print("answered:", sum(1 for concept in TASKS.values() if concept != "?"), "of", len(TASKS))

What to learn next

You laid the whole toolbox out on the bench: the five core ideas everything else sits on, the four collections and how to choose between them, the habits that make code hold up, the power features and the complaint each one answers, and the domains you can now point Python at. The exercise then drilled the skill that actually survives: recognizing which tool a problem is asking for.

Recognition is one half. The other is what to do when something breaks and you do not recognize it at all. A Debugging Playbook That Always Works is the method: reproduce, read the traceback backwards, shrink, and change one thing at a time.