Worked examples — `range()` — start, stop, step
This page is a drill. We take the rule from the parent note and pound it against every kind of input range() can meet. First we lay out a map of all the cases; then we walk each one, forecasting before we compute.
Recall The two rules we lean on the whole way
Term rule: the -th number () is .
Keep-going rule: if step > 0 keep while ; if step < 0 keep while .
Count: .
Here means "round up to the nearest whole number" (the ceiling). Look at the number line: , , . We round up because even a tiny leftover distance needs one more term to land in the range.
Why the max(0, …) clamp? The ceiling alone can go negative — this happens exactly when you point the wrong way (a positive step but stop < start, or a negative step but stop > start). A negative "count" is nonsense; there are simply no terms. The max(0, …) catches every such degenerate case and reports 0. Without it, the formula would return a meaningless negative number for Cells F, G, and the empty subcases of A and B below. It is not decoration — it is what makes the single formula correct for all inputs at once.
The scenario matrix
Every call range(...) falls into exactly one of these cells. Our examples below are labelled with the cell they cover, and together they hit all of them.
| # | Cell (case class) | Direction | Example we use |
|---|---|---|---|
| A | Single arg, defaults | up, step | range(6) |
| A0 | Single arg, non-positive stop (empty) | none | range(-3) |
| B | Two args, custom start | up, step | range(3, 8) |
| B0 | Two args, start > stop (empty) | mismatch | range(5, 3) |
| C | Three args, step does NOT divide gap | up, step | range(2, 12, 3) |
| D | Three args, big step overshoots early | up, step | range(1, 10, 4) |
| E | Negative step (counting down) | down, step | range(9, 1, -2) |
| F | Degenerate — empty (start = stop) | none | range(4, 4) |
| G | Degenerate — empty (wrong direction) | mismatch | range(0, 5, -1) |
| H | Negative start / crossing zero | up through 0 | range(-3, 4, 2) |
| I | Error inputs (step 0, floats) | invalid | range(0, 5, 0) |
| J | Word problem (real-world spacing) | up, step | seats in a row |
| K | Exam twist (include the endpoint) | up | range(1, N+1) |

The picture above is our compass for every example: dots are the numbers range produces, the red hollow circle is stop which is never produced. Whenever you feel lost, redraw this line.
Example A — single argument, all defaults
Steps.
- Only one argument means Python fills in the hidden two:
start = 0,step = 1. So this is secretlyrange(0, 6, 1). Why this step? The single-argument form is a shorthand; making the hidden defaults visible removes all guesswork. - Terms: . So Why this step? Plugging into the term rule turns "range" into an actual list of numbers.
- Keep while : that is . The next term is not , so we stop.
Why this step? The keep-going rule with
step > 0uses strict "", which is exactly whystopis excluded.
Answer: [0, 1, 2, 3, 4, 5].
Verify: count . And indeed len(range(6)) == 6 — the "no surprises" law: len(range(N)) == N.
Example A0 — single argument, non-positive stop
Steps.
- Hidden defaults again:
start = 0,step = 1. So this isrange(0, -3, 1). Why this step? The single-argument form always starts at 0 and steps up by 1 — even when that makes no sense for the stop you gave. - . Keep while ? But → the very first candidate already fails. Why this step? We are trying to climb upward from 0 to a wall that sits below us — impossible.
Answer: [] (empty).
Verify: ✓. This is the reason range(0) and any range(N) with are empty — a common surprise for beginners who forget start is fixed at 0.
Example B — custom start, default step
Steps.
- Two arguments =
start, stop; step defaults to1. Sorange(3, 8, 1). Why this step? With two args Python never guesses the start — you gave it (3). Only step is filled in. - . Keep while : . is excluded. Why this step? Same half-open logic as A, just shifted to start at 3.
Answer: [3, 4, 5, 6, 7].
Verify: . Five numbers ✓.
Example B0 — two arguments, start greater than stop
Steps.
- Two arguments → step defaults to
+1. Sorange(5, 3, 1). Python will try to climb upward from 5. Why this step? The default step is always ; there is no automatic "reverse if start > stop". - . Keep while ? But → stop immediately. Why this step? We start above the wall while trying to move up — we are already past it, so nothing is produced.
Answer: [] (empty).
Verify: ✓. To actually count down from 5 to 3 you must supply a negative step: range(5, 3, -1) → [5, 4].
Example C — step where the step does NOT divide the gap
Steps.
- : gives Why this step? The term rule generates the arithmetic ladder before we test the wall.
- Keep while : , , , , then → stop.
Why this step? We test each rung against
stop; 11 squeaks in, 14 does not. Note the last term 11 lands below the wall with 1 leftover — the step never lands on 12.
Answer: [2, 5, 8, 11].
Verify: . Four numbers ✓. The gap is not a multiple of the step , yet the formula still nails it because the ceiling rounds the leftover up into one whole term.
Example D — big step that overshoots the stop early
Steps.
- : Why this step? Generate the ladder first.
- Keep while : ; then → stop. Why this step? 9 is the last rung under the wall at 10.
Answer: [1, 5, 9].
Verify: ✓.
Example E — negative step (counting down)

Steps.
- : Why this step? Same term rule — a negative step simply makes the ladder go downhill, as the red arrows in the figure show.
- Because
step < 0, the keep-going rule flips to "": keep while . Why this step? When descending, "not yet at the wall" means being above it, not below. The strict "" again excludes the endpoint. - Check: ✓, ✓, ✓, ✓, then → stop. So
1is excluded. Why this step?stop = 1is a wall in both directions — excluded whether we climb or descend.
Answer: [9, 7, 5, 3].
Verify: ✓. Note both top and bottom of the fraction are negative, so their ratio is positive — the formula handles direction automatically.
Example F — degenerate: start equals stop
Steps.
- . Keep while ? But . Why this step? The very first candidate already fails the strict "" test.
Answer: [] (empty).
Verify: ✓. Zero elements — a perfectly legal empty range.
Example G — degenerate: step points the wrong way
Steps.
- — heading away from 5. Why this step? A negative step from below the target can never climb up to it.
step < 0, so keep while . But → stop immediately. Why this step? The keep-going rule is never satisfied; nothing is produced.
Answer: [] (empty).
Verify: ✓. The negative ceiling is where max(0, …) earns its keep — it clamps nonsense to "no elements".
Example H — negative start, crossing zero
Steps.
- : Why this step? Negative starts are nothing special — the term rule marches upward from wherever start sits.
- Keep while : ; then → stop. Why this step? The wall at 4 is checked the same way; zero is skipped only because our ladder happens to step over it.
Answer: [-3, -1, 1, 3].
Verify: ✓. (Notice: 0 is absent — the ladder never lands on it.)
Example I — the error cases
Steps.
range(0, 5, 0)— step is zero. The ladder never moves off 0, so it would loop forever. Why this step? An infinite sequence is useless, so Python refuses up front:ValueError: range() arg 3 must not be zero.range(0, 1, 0.1)— the argument0.1is a float, not an integer. Why this step?rangeis an integer counting machine only. It raisesTypeError: 'float' object cannot be interpreted as an integer. For float steps use numpy.arange or linspace.
Answer: both raise, never return a list.
Verify: logic only (no numeric output) — the count formula does not even apply because the inputs are rejected before any term is generated.
Example J — word problem (real-world spacing)
Steps.
- First reserved seat is 5 →
start = 5. Every 3rd seat →step = 3. Why this step? "First" maps to start, "every 3rd" is the hop size. - "up to but not past 24" — we want to include 24 if it lands on the pattern. Since
stopis excluded, setstop = 25so 24 can appear. Why this step? To include an endpoint we always pass (this is Cell K's trick). Here , sostop = 25. - Call:
range(5, 25, 3). Ladder: ; keep while : . Why this step? survives; stops. Note 24 is not in the pattern, so it doesn't appear even though we made room for it — the pattern decides, the wall only limits.
Answer: [5, 8, 11, 14, 17, 20, 23].
Verify: ✓. Seven reserved seats.
Example K — exam twist: force the endpoint IN
Steps.
range(1, 6)gives — the wall at 6 excludes 6. Why this step? Half-open[1, 6)never reaches its upper bound.- To include , push the wall one past it:
range(1, N+1)=range(1, 7). Why this step? Excluding leaves — the endpoint now sits safely inside.
Answer: correct call is range(1, 7) → [1, 2, 3, 4, 5, 6].
Verify: ✓ — exactly numbers, ending on .
Active recall
Cell C: list(range(2, 12, 3))?
[2, 5, 8, 11]Cell E: list(range(9, 1, -2))?
[9, 7, 5, 3]Cell H: list(range(-3, 4, 2))?
[-3, -1, 1, 3]Cell A0: list(range(-3))?
[] — single-arg start is fixed at 0, can't climb up to −3.Cell B0: why is range(5, 3) empty?
Cell G: why is range(0, 5, -1) empty?
Cell J: range for seats 5,8,…,≤24?
range(5, 25, 3) → [5,8,11,14,17,20,23]Cell K: include N in range(1, N)?
range(1, N+1).Cell I: what does step = 0 do?
ValueError (would loop forever).Why the max(0) clamp in the count formula?
Connections
- Back to the parent topic.
- for loops — every example here is a loop driver in disguise.
- Lists and list() — we wrapped each range in
list()to see it. - Indexing and slicing — shares the identical half-open
[start, stop)convention. - Arithmetic sequences — is the discrete arithmetic progression behind all cells.
- numpy.arange / linspace — the float-capable cousins for Cell I.
- enumerate() — when you need index and value together.