1.2.19 · D3Introduction to Programming (Python)

Worked examples — `range()` — start, stop, step

2,902 words13 min readBack to topic

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)

Figure — `range()` — start, stop, step

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.

  1. Only one argument means Python fills in the hidden two: start = 0, step = 1. So this is secretly range(0, 6, 1). Why this step? The single-argument form is a shorthand; making the hidden defaults visible removes all guesswork.
  2. Terms: . So Why this step? Plugging into the term rule turns "range" into an actual list of numbers.
  3. Keep while : that is . The next term is not , so we stop. Why this step? The keep-going rule with step > 0 uses strict "", which is exactly why stop is 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.

  1. Hidden defaults again: start = 0, step = 1. So this is range(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.
  2. . 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.

  1. Two arguments = start, stop; step defaults to 1. So range(3, 8, 1). Why this step? With two args Python never guesses the start — you gave it (3). Only step is filled in.
  2. . 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.

  1. Two arguments → step defaults to +1. So range(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".
  2. . 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.

  1. : gives Why this step? The term rule generates the arithmetic ladder before we test the wall.
  2. 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.

  1. : Why this step? Generate the ladder first.
  2. 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)

Figure — `range()` — start, stop, step

Steps.

  1. : Why this step? Same term rule — a negative step simply makes the ladder go downhill, as the red arrows in the figure show.
  2. 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.
  3. Check: ✓, ✓, ✓, ✓, then → stop. So 1 is excluded. Why this step? stop = 1 is 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.

  1. . 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.

  1. — heading away from 5. Why this step? A negative step from below the target can never climb up to it.
  2. 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.

  1. : Why this step? Negative starts are nothing special — the term rule marches upward from wherever start sits.
  2. 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.

  1. 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.
  2. range(0, 1, 0.1) — the argument 0.1 is a float, not an integer. Why this step? range is an integer counting machine only. It raises TypeError: '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.

  1. 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.
  2. "up to but not past 24" — we want to include 24 if it lands on the pattern. Since stop is excluded, set stop = 25 so 24 can appear. Why this step? To include an endpoint we always pass (this is Cell K's trick). Here , so stop = 25.
  3. 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.

  1. range(1, 6) gives — the wall at 6 excludes 6. Why this step? Half-open [1, 6) never reaches its upper bound.
  2. 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?
default step +1 tries to climb up from 5 to 3 — impossible.
Cell G: why is range(0, 5, -1) empty?
negative step can't climb from 0 up to 5 — count clamps to 0.
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)?
use range(1, N+1).
Cell I: what does step = 0 do?
raises ValueError (would loop forever).
Why the max(0) clamp in the count formula?
the ceiling goes negative for wrong-direction inputs; max(0) reports 0 terms instead of nonsense.

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.