Strings
You have used text since the first module, as colour names and as words to print. So far a piece of text has been one whole thing. In this module you open it up: a string is a row of characters, each with a position, and you can take pieces out of it, walk along it, search it and build a new one.
Before this, you can: write a function that returns a value; loop with range and use the loop variable; tell a number from text.
By the end you will be able to pick out a character or a piece of a string, loop over its characters, ask whether one piece of text is inside another, use methods that give back a new string, build a string step by step, and put numbers into text with an f-string.
Before you read on, decide what this prints:
title = "the sea"
title.upper()
print(title)
the sea
If you expected THE SEA, you are in good company. title.upper() does make "THE SEA". But it makes it as a new string and hands it back, and this program does nothing with what it was handed. title is still the string it always was. A string is never changed in place, in Python as in Java. Most of this module follows from that.
Text has positions
A string is a row of characters. Each has a position, counted from 0:
p l a n e t
0 1 2 3 4 5
Square brackets pick out one character, and len counts them all:
word = "planet"
print(word[0], word[5], len(word))
p t 6
The last position is always one less than the length, because counting starts at 0. Asking for a position that is not there is an error:
word = "planet"
print(word[6])
File "your_program.py", line 2
print(word[6])
IndexError: string index out of range
What you get from word[0] is itself a string, one character long. Python has no separate type for a single character.
Two strings are compared with ==, and it compares the text itself, character by character. "cat" == "cat" is True and "Cat" == "cat" is False, because a capital letter is a different character.
From the end, and in pieces
A negative position counts from the end: word[-1] is the last character, word[-2] the one before it.
Two positions with a colon between them take a piece, called a slice. word[1:4] starts at position 1 and stops before position 4:
word = "planet"
print(word[-1])
print(word[1:4])
print(word[:2])
print(word[3:])
t
lan
pl
net
Leave out the first number and the slice starts at the beginning. Leave out the second and it runs to the end. A useful way to read word[1:4] is to think of the numbers as marking the gaps between the tiles: cut at gap 1 and at gap 4, and take what is between.
p l a n e t
0 1 2 3 4 5 6
That is why word[:2] and word[2:] together give back the whole word, with nothing lost and nothing twice. It is also why the length of word[1:4] is 4 − 1.
A slice that reaches past the end is not an error; it just stops at the end. word[4:100] is "et".
One character at a time
A for loop can walk along a string directly. The loop variable is given each character in turn:
for ch in "sun":
print(ch)
s
u
n
There is no need for range here, and no position to get wrong. Use this form when you only need the characters.
When you need the position as well, loop over the positions and look each character up:
word = "sun"
for i in range(len(word)):
print(i, word[i])
0 s
1 u
2 n
Either loop can draw. Here each character gets a tile:
from scimigo import canvas, rectangle, text
canvas(600, 400)
word = "sun"
for i in range(len(word)):
rectangle(40 + i * 70, 160, 60, 60, color="khaki")
text(58 + i * 70, 202, word[i], size=32)
Is it in there?
in asks whether one string appears inside another, as a run of characters next to each other. The answer is True or False:
print("an" in "planet")
print("p" in "Planet")
print("ant" in "planet")
True
False
False
"an" is there, at positions 2 and 3. "p" is not in "Planet", which has a capital P. And "ant" is not there although all three letters are, because they are not next to each other in that order.
in is often the shortest way to test a character against several possibilities. ch in "aeiou" is True when ch is any one of the five vowels.
Methods give you a new string
A method is a function that belongs to a value, called with a dot. Strings have many. Three useful ones:
messy = " Hello_World "
print(messy.strip())
print(messy.lower())
print(messy.replace("_", " "))
print(messy)
Hello_World
hello_world
Hello World
Hello_World
strip takes the spaces off both ends, lower makes every letter lower case, and replace swaps one piece of text for another wherever it appears. Look at the last line: after all three calls, messy is exactly what it was. Each method made a new string and handed it back. None of them changed messy, because a string cannot be changed.
So a method call on a line of its own does nothing useful. To keep the result, give it a name. To apply several steps, keep each result and work on that:
messy = " Hello_World "
tidy = messy.strip()
tidy = tidy.lower()
tidy = tidy.replace("_", " ")
print(tidy)
hello world
This is the same lesson as in Functions: a value that is handed back and not kept is lost.
Building text
Since a string cannot be changed, a longer one is made by joining: + puts two strings end to end and gives a new string. Doing that in a loop builds a result piece by piece, the same way a running total is built:
result = ""
for ch in "abc":
result = result + ch + "-"
print(result)
a-b-c-
result starts as the empty string, "", which plays the part that 0 plays in a total. On each turn a new, longer string is made and the name result is moved to it.
The order matters. result + ch adds at the end; ch + result adds at the front, which builds the text backwards.
Numbers into text, and back
+ joins two strings or adds two numbers. It will not mix them:
score = 7
print("Score: " + score)
File "your_program.py", line 2
print("Score: " + score)
TypeError: can only concatenate str (not "int") to str
Python will not guess whether you meant the text "7" or the number 7. str makes text from a number, and int makes a whole number from text that looks like one:
print("Score: " + str(7))
print(int("12") + 1)
print("12" + "1")
Score: 7
13
121
Most of the time there is a better tool. Put the letter f in front of the opening quote, and anything inside curly brackets is worked out and placed in the text:
name = "Ada"
width = 3
height = 4
print(f"{name} drew {width} by {height}, area {width * height}.")
Ada drew 3 by 4, area 12.
This is an f-string. The brackets can hold a name or a whole calculation, and numbers need no str. When you want to print or draw a mixture of text and values, reach for an f-string first.
In the lab
You will read characters and slices before running them, put every letter of a word on its own tile, decide what in answers, find the line that throws a result away, tidy a messy string in three steps, follow a result as it grows, and then write functions that count vowels and hide all but the last four characters of a code.