Python, one concept per page
The twenty-five ideas that take you from never coded to running real programs.
A diagram, the classic mistake, and two lines to type. That's a page.
Python, one concept per page
The twenty-five ideas that take you from never coded to running real programs.
Set in Space Grotesk, Inter and JetBrains Mono (SIL Open Font License).
Language behaviour verified against the official Python documentation: docs.python.org/3 (tutorial and library reference).
Python and the Python logo are trademarks of the Python Software Foundation. This book is an independent guide and is not affiliated with or endorsed by the PSF.
Your purchase is for personal use only. You do not have redistribution rights: please do not share, resell, or republish this book or its pages.
© 2026 Steve Hodgkiss. All rights reserved. Personal use only; no redistribution rights.
Edition 1.0 · stevehodgkiss.net
Contents
First steps
The five moves behind every first program: say something, remember something, ask, tell apart, and join text.
- 01print
- 02Variables
- 03input
print() turns what you give it into text, joins the pieces with spaces, and writes them to the screen followed by a newline. It is the fastest feedback loop in programming.
Every session starts here. You type a line, press Enter, and the answer appears immediately. No compile step, no ceremony. That instant reply is what makes Python a good place to learn: you can check every idea as soon as you have it.
The classic confusion: print("2" + "3") prints 23, not 5. Both were text, and + joins text. Nothing is broken; the machine did exactly what was asked.
Numbers to add, quotes to join. Print either kind.
If you can see what your program is thinking, you can fix it. print is how you look.
print("Hello, world!") print("Total:", 2 + 3)
The = sign attaches a name to a value so you can use it later. It is a label pointing at a box, not a statement that two things are equal.
Variables
School maths taught you = means equal, so x = x + 1 looks impossible. In Python it reads right to left: work out the right side, then point the name at the result. The old value is simply left behind.
Give things names that say what they mean: total_cost beats tc. The name is for the next person who reads the code, and that person is usually you, three weeks later.
name = value is the whole grammar. You will type it ten thousand times.
Names are labels you move. Values are what they point at.
price = 4.50 price = price + 1 print(price) # 5.5
input() pauses the program, shows its prompt, reads one line of typing, and returns it as a string, newline stripped. Numbers you type arrive as text until you convert them.
input
Programs get interesting the moment they respond to a person. input() shows its prompt, waits for a line, and hands it back as text, exactly as typed.
The classic crash comes one line later: age = input() then age + 1 explodes with TypeError, because "30" is a string and Python refuses to guess.
Convert at the door, where the intent is obvious: int() for whole numbers, float() for decimals.
input hands you text. Say out loud what you actually want, and convert there.
name = input("Name? ") age = int(input("Age? ")) print("Hi", name, "- next year:", age + 1)
Collections
Real programs juggle lists of things. These five ideas cover almost all of it.
- 01Lists
- 02in and for
A list is an ordered sequence written in square brackets. Positions are numbered starting at 0, square brackets read one position, and negative indexes count from the end. Lists are mutable: you can change what a slot holds.
Lists
One variable per thing stops scaling at about three things. A list holds any number of values in one name, keeps them in order, and gives each a numbered slot.
The number that trips everyone: counting starts at 0, so the first item is pets[0] and a six-item list ends at pets[5]. Ask for pets[6] and Python answers IndexError, not a polite nothing.
Negative indexes are the friendly shortcut: pets[-1] is always the last item, however long the list grew.
Six things, slots zero to five. The fencepost is the whole trick.
pets = ["cat", "dog", "owl", "fox"] print(pets[0], pets[-1], len(pets))
The for loop visits each item of any sequence in order, handing it to you under a name you choose. The in operator asks whether a value is anywhere in a collection and answers True or False.
in and for
Here is the loop you will write most: for pet in pets: and an indented line that runs once per item. Python hands you each value in order, under the name you invented.
Habits carried over from other languages: you almost never need for i in range(len(pets)) just to read items. If you only need the values, walk the values.
Before the loop, in does the quick check: if "owl" in pets: Dictionaries join in with for k, v in d.items():
pets = ["cat", "dog", "owl"] print("owl" in pets) for pet in pets: print(pet.upper())
An if chain tests each condition from the top and runs only the first true branch; else is the optional catch-all. elif is short for else if. Comparisons use ==, assignment uses =.
if elif else
This is where a program stops being a receipt and starts making decisions. Each test is checked top to bottom; the first true one runs and the rest are skipped. Order is therefore meaning.
Grammar note, not maths note: one = stores, two == compares. Writing if score = 70: is a SyntaxError, and it is the single most common typo in the language.
Chain with elif when categories exclude each other; let else catch the rest. First true test wins.
score = 72 if score >= 70: print("pass") elif score >= 50: print("borderline") else: print("fail")
Real programs
Files, failures, other people's code, and a safe place to install it all.
- 01Reading a file
- 02try and except
open(filename) returns a file object you can read from: the whole file, one line, or line by line in a for loop. A with block closes the file automatically when the block ends, even on an error.
Reading a file
Everything interesting eventually lives in a file: spreadsheet exports, logs, a novel. open it, pick how much to read, and let with close it behind you.
The fuss that follows every first attempt: lines arrive with an invisible \n on the end, so "cat" and "cat\n" never match. It looks like a ghost bug.
line.strip() trims the ends before you compare, and the with line means you never leak a half-open file again.
Open, read, close. The with line makes the close automatic.
with open("notes.txt") as f: for line in f: print(line.strip())
If a statement in a try block raises an exception, the rest of the block is skipped and the matching except clause runs instead; execution then continues after the try/except. Unmatched exceptions still stop the program.
try and except
Someday a user will type "seven" where a number goes. try wraps the line that can fail; except is the planned response, and the program keeps running with its dignity intact.
The well-meant version that backfires: a bare except:, which also swallows your own typos and logic bugs as silent None-shaped mysteries.
Name the error you expect: except ValueError:, so the surprises still surface loudly. The classic loop asks until the answer parses.
Errors are not failure. An unhandled one is.
try: age = int(input("Age? ")) print("Next year:", age + 1) except ValueError: print("That was not a number.")