Python range(): len(), Indexing, in and reversed()
A range object is what range() returns: a compact sequence of whole numbers you can measure, index and search without looping over it.
Goal: answer questions about a range, such as "how many?", "what is last?" and "is this number in it?", without a loop.
Read The range() Function first. This lesson also uses len(), indexing and in from Lists. Every block runs on its own with fixed numbers and needs no setup.
See what a range really is
range() does not build a list. It stores only its start, stop and step, and works out each number when asked.
numbers = range(1, 11)
print(numbers)
print(list(numbers))
The first line prints range(1, 11), not the numbers. list() asks for every value, so the second line prints [1, 2, ..., 10].
Measure and index a range
A range supports len(), indexing and slicing, just like a list.
evens = range(0, 20, 2)
print(len(evens))
print(evens[0], evens[-1])
print(evens[2:5])
This prints 10, then 0 18, then range(4, 10, 2). A slice of a range is another range. Python computes all of these from start, stop and step, so len(range(1_000_000_000)) is instant.
Check membership with in
in tells you whether a number is one of the range's values.
evens = range(0, 20, 2)
print(6 in evens)
print(7 in evens)
print(20 in evens)
This prints True, False, False. 7 is skipped by the step, and 20 is the excluded stop. For whole numbers this is a quick calculation, not a search through every value.
Choosing reversed() or a negative step
Both count down. reversed() keeps the range you already think in; a negative step makes you shift both ends by one.
print(list(reversed(range(1, 6))))
print(list(range(5, 0, -1)))
print(list(range(5, 1, -1)))
The first two both print [5, 4, 3, 2, 1]. The third shows the usual slip: with stop 1 the 1 is left out, so it prints [5, 4, 3, 2]. Prefer reversed() when you start from an upward range, such as reversed(range(len(word))).
Watch out: empty ranges and whole numbers only
A range whose step points away from its stop is empty, with no error. A range also needs whole numbers.
print(len(range(5, 1)))
print(list(range(1, 5, -1)))
print(range(0, 1, 0.5)) # this line raises TypeError
The first two print 0 and []: the loop body would simply never run. The last line raises TypeError because 0.5 is not an int. A step of 0 raises ValueError.
Note: these examples keep ranges small and fixed. A range of a billion numbers costs almost no memory, but list() of it would try to store them all.
Your turn
Finish describe() so it prints how many values r holds, its last value and whether target is one of them. Use len(), an index and in — no loop.
def describe(r, target):
# print len(r), the last value of r, and whether target is in r
pass
describe(range(3, 30, 3), 12)
describe(range(10, 0, -2), 1)
Expected output
9 27 True
5 2 False