Two Ways to Walk a String
You met strings before you met loops, so everything the strings lesson could show you was a whole string at a time — one method, one answer. A loop changes that: it can visit a string one character at a time, and then the questions that have no method of their own become easy. There are two ways to write that loop, and they are not the same.
1Why a loop can walk a string at all
Because a string is a sequence — an ordered run of characters, with a first, a second and a last. That is the same fact that lets you write word[0], len(word) and word[1:4], and it is what a for loop needs. A for loop does not only count with range(): give it any sequence and it hands out that sequence's items, in order, one per round.
2The two loops, and their identical output
Here is the first, and the shorter of the two:
# the loop hands out one character at a time
word = 'PYTHON'
for ch in word:
print(ch)P Y T H O N
And here is the second, which counts positions and looks each one up:
# the loop hands out one position at a time
word = 'PYTHON'
for i in range(len(word)):
print(word[i])P Y T H O N
The output is identical, which is exactly the problem with meeting these two on separate pages: they look like two spellings of one idea, and a student picks whichever they saw last. The difference is not in what is printed. It is in what the loop variable holds.
3Watch both variables at once
Step the two loops forward together and keep your eye on the two boxes. On the left ch fills with a letter. On the right i fills with a number, and the letter has to be fetched separately with word[i].
Same output, different loop variable. Watch what each one holds.
Nothing has run yet.
Nothing has run yet. range(len(word)) is range(0, 6).
Both loops are about to run over the same string, and both will print the same six lines. The difference is entirely in what the loop variable holds.
4What actually differs
| by character | by index | |
|---|---|---|
| the header | for ch in word: | for i in range(len(word)): |
| the loop variable holds | a character, 'P' | a number, 0 |
| to use the character | ch | word[i] |
| knows its position? | no | yes |
| can reach the next one? | no | yes — word[i + 1] |
| shorter to write? | yes | no |
5When you want both at once
The index form gives you both, because the character is only ever one lookup away. That is how a program prints a string with its positions underneath — the picture the widget above draws:
# with the position, you can print both
word = 'PYTHON'
for i in range(len(word)):
print(i, word[i])0 P 1 Y 2 T 3 H 4 O 5 N
range(len(word)), not range(word). range() counts numbers, so it has to be handed the length. Giving it the string itself raises TypeError: 'str' object cannot be interpreted as an integer. And it is len(word), not len(word) - 1: range() already leaves its stop out, so subtracting one as well drops the last character.6Recap
So a for loop can hand out its characters, in order, the same way it hands out the values of a list or a range.
It is the word exam questions use for looping over a string one character at a time.
Shorter, safer, and enough for counting, testing and building. It has no idea where it is in the string.
The character costs one lookup, word[i] — and in exchange you know the position and can reach the neighbours.
In for ch in 'PYTHON': print(ch), what does ch hold on the third round?
Which loop can compare each character with the one after it?
Why is it range(len(word)) rather than range(len(word) - 1)?