Why does rangestart, end not include end?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Python’s range(start, end) does not include end because it follows the half-open interval convention: include the start, exclude the stop. This is not arbitrary. It makes counting, indexing, slicing, and composing adjacent ranges simpler and less error-prone.
What range Actually Produces
A quick example shows the behavior:
Output:
The stop value marks the point where iteration stops, not the last included element.
Half-Open Intervals Match Counting
If you want exactly n values starting at zero, range(n) gives them naturally.
That produces 0, 1, 2, 3, 4, which is five numbers. If the end were included, range(5) would produce six values and create constant off-by-one confusion.
This is one of the main reasons the design is useful: the stop parameter doubles as the length when the start is zero.
It Aligns with Zero-Based Indexing
Python sequences are zero-indexed, so valid indices for a sequence of length n are 0 through n - 1. That means range(len(sequence)) lines up exactly with the legal index positions.
Because the stop is excluded, range(len(names)) never tries to access names[3], which would be out of bounds.
Adjacent Ranges Fit Together Cleanly
Half-open intervals compose without overlap or gaps.
The second range starts exactly where the first one stops. If both ends were inclusive, you would need extra adjustment logic to avoid duplication.
This becomes especially valuable in chunking, pagination, and interval-based algorithms.
It Matches Slice Semantics
Python slicing also uses a start-inclusive, stop-exclusive convention.
This consistency matters. Once you understand range(start, stop), list slices, string slices, and many index-based loops follow the same mental model.
Length Is Simple to Compute
With a half-open interval, the number of elements is just:
stop - start
For example, range(3, 8) has 8 - 3 = 5 values:
If the stop were included, length calculations would constantly need + 1, which complicates both reasoning and code.
Inclusive Ranges Are Still Possible
If you genuinely need the endpoint included, just adjust the stop value.
For descending ranges:
Python does not block inclusive behavior. It simply chooses a more useful default convention and lets you shift the stop boundary when needed.
It Helps Prevent Off-By-One Errors
Paradoxically, many beginners think excluding the end causes off-by-one errors, when in practice it often prevents them. The convention gives you a stable rule:
- start is included
- stop is excluded
Once you internalize that rule, loops and slices become more predictable.
The bigger source of bugs is forgetting which convention an API uses and mixing inclusive and exclusive boundaries inconsistently across the same piece of code.
A Practical Example: Chunking a List
Half-open intervals make chunk boundaries straightforward:
Because slicing excludes end, the chunk logic stays clean and does not need manual overlap corrections.
Common Pitfalls
The most common mistake is reading end as “last included value” instead of “stop boundary.” Another is trying to force inclusive reasoning everywhere rather than using Python’s half-open convention consistently. Developers also sometimes forget to add one when they truly want an inclusive endpoint. Finally, mixing APIs that use different interval conventions is a common source of confusion unless you name variables clearly as start and stop.
Summary
- '
range(start, stop)uses a start-inclusive, stop-exclusive convention.' - This matches zero-based indexing and sequence slicing.
- Adjacent ranges fit together cleanly without overlap.
- The number of produced values is easy to reason about as
stop - start. - If you need an inclusive endpoint, adjust the stop value explicitly.

