Picking out one character is useful. Visiting every character, one after the other, is what lets a program count letters, search for a space or check each character of a password. That is a job for a loop.
A string of length 6 has indices 0 to 5, and range(len(word)) produces
exactly those numbers. So a for loop over that range visits every position in
the string:
word = "Python"
for i in range(len(word)):
print(word[i])
That prints the six letters of Python, one per line. It is the same loop you
used to output every item in a list: len says how many there are, range
turns that into the indices, and the counter picks out one each time round.
The number of repetitions is known before the loop starts, so this is a
count controlled loop.
The character can be taken with a slice as well. word[i:i+1] starts at i
and stops before i + 1, so it is a substring exactly one character long, and
this loop prints the same six lines:
word = "Python"
for i in range(len(word)):
print(word[i:i+1])
Both forms are correct. Exam questions often describe this as extracting one
character at a time with a substring, and word[i:i+1] is the Python for that
description.
Put an if inside the loop and a running total around it, and the loop can
count the characters that match:
word = "Mississippi"
count = 0
for i in range(len(word)):
if word[i] == "s":
count = count + 1
print("Number of s: " + str(count))
This is the same running-total pattern as the total you built with a for loop: the total is created before the loop, it grows inside the loop, and it is printed after the loop.
The comparison is exact, so "S" == "s" is false. To count capital and small
letters alike, convert the whole word to one case with upper() or lower()
before the loop starts.
Sometimes you need to know where a character is, not just whether it is there. The loop counter already holds the position, so store it when the character turns up:
email = "[email protected]" at_position = 0 for i in range(len(email)): if email[i] == "@": at_position = i print("The @ is at index " + str(at_position)) print("Name: " + email[:at_position]) print("Domain: " + email[at_position + 1:])
Once the position is known, slicing does the rest. Everything before it is
email[:at_position], and everything after it starts one place further along,
at at_position + 1.
A loop can go inside another loop. The outer loop picks out one string at a time from a list, and the inner loop then works through the characters of that string:
words = ["tree", "apple", "sky"]
for w in range(len(words)):
word = words[w]
count = 0
for i in range(len(word)):
if word[i] == "e":
count = count + 1
print(word + ": " + str(count))
Read it from the outside in. The outer loop runs three times, once for each
word. Every time it runs, the inner loop runs all the way through the
letters of that one word, so it runs four times for tree, five times for
apple and three times for sky.
Three details make it work:
w and i. If both were called i,
the inner loop would overwrite the outer loop’s position.count = 0 is inside the outer loop, so each word starts counting from zero.
Move it above the outer loop and the counts pile up across all three words.print is indented once, not twice. It belongs to the outer loop, so it
runs once per word, after the inner loop has finished with that word.A loop inside a loop is called a nested loop.
When a loop only needs each character and never its position, Python has a shorter way to write it:
word = "Python"
for letter in word:
print(letter)
This prints the same six lines as the first loop on this page. There is no
range and no len here: the counter, letter, holds each character itself,
one after the other, instead of holding its index.
The short form is easier to read, but it cannot tell you where a character is.
Finding the @ in an email address needs the index, so that loop has to use
range(len(email)).
Make sure that you check for the following things:
range(len(word)), not over range(word) or
range(len(word) - 1).word[i] or word[i:i+1], with the counter
as the index, not a fixed number.