Strings are not like integers, floats, and booleans. A string is a sequence, which means it is an ordered collection of other values.

Index

team = 'New England Patriots'
letter = team[1] #The expression in brackets is called an index. 
print(letter)

Q. What's the first letter?

A. The first letter is the 0th letter.

print(team[0])
print(team[1.5])

len

len is a built-in function that returns the number of characters in a string:

len(team)

Q. How do we find the last letter?

last = team[len(team)-1]
print(last)
last = team[-1]
print(last)

Traversal with a for loop

lots of computations involve processing a string one character at a time. Often they start at the beginning, select each character in turn, do something to it, and continue until the end. This pattern of processing is called a traversal. One way to write a traversal is with a while loop:

index = 0
while index < len(team):
    letter = team[index]
    print(letter)
    index = index + 1
for letter in team:
    print(letter)

Do you know their names?

In Robert McCloskey’s book Make Way for Ducklings, the names of the ducklings are Jack, Kack, Lack, Mack, Nack, Ouack, Pack, and Quack. This loop outputs these names in order:

prefixes = 'JKLMNOPQ'
suffix = 'ack'
for letter in prefixes:
    print(letter + suffix)
Jack
Kack
Lack
Mack
Nack
Oack
Pack
Qack

Exercise 01

Modify the program to fix the names of Ouack and Quack.

String slicing

A segment of a string is called a slice. Selecting a slice is similar to selecting a character:

team = 'New England Patriots'
print(team[0:11])
print(team[12:20])

If you omit the first index (before the colon), the slice starts at the beginning of the string. If you omit the second index, the slice goes to the end of the string:

print(team[:11])
print(team[12:])
team[4:3]

Q. what do you think team[:] means?

A string slice can take a third index that specifies the “step size”; that is, the number of spaces between successive characters. A step size of 2 means every other character; 3 means every third, etc.

team[::2]

Strings are immutable

team = 'New England Patriots'
team[12:20] ='Seahawks'

The reason for the error is that strings are immutable, which means you can’t change an existing string. The best you can do is create a new string that is a variation on the original:

new_team = team[:12]+'Seahawks'
print(new_team)

This example concatenates a slice of team onto 'Seahawks'. It has no effect on the original string team.

print(team)

Searching

What does the following function do?

def find(word, letter):
    index = 0
    while index < len(word):
        if word[index] == letter:
            return index
        index = index + 1
    return -1
print(find(team, 'E'))

This pattern of computation — traversing a sequence and returning when we find what we are looking for — is called a search.

Looping and counting

What does the following code do?

word = 'New England Patriots'
count = 0
for letter in word:
    if letter == 'a':
        count = count + 1
print(count)

This program demonstrates another pattern of computation called a counter. The variable count is initialized to 0 and then incremented each time an a is found. When the loop exits, count contains the result — the total number of a’s.

Exercise 02

Encapsulate this code in a function named count, and generalize it so that it accepts the string and the letter as arguments.

String methods

Strings provide methods that perform a variety of useful operations. A method is similar to a function—it takes arguments and returns a value—but the syntax is different. For example, the method upper takes a string and returns a new string with all uppercase letters.

We will be learning more about method in Object-Oriented Programming section.

team = 'New England Patriots'
new_team = team.upper()
print(new_team)

This form of dot notation specifies the name of the method, upper, and the name of the string to apply the method to team. The empty parentheses indicate that this method takes no arguments.

A method call is called an invocation; in this case, we would say that we are invoking upper on team.

team = 'New England Patriots'
index = team.find('a')
print(index)
print(team.find('En'))
print(team.find('a', 10)) 
# Read the documentation of this function to find out
# what it does.
# https://docs.python.org/3/library/stdtypes.html#str.find

This is an example of an optional argument; find can also take a third argument, the index where it should stop:

name = 'bob'
print(name.find('b', 1, 2))

Exercise 03

Read the documentation of the string methods at https://docs.python.org/3/library/stdtypes.html#string-methods. You might want to experiment with some of them to make sure you understand how they work. split, strip and replace are particularly useful.

The documentation uses a syntax that might be confusing. For example, in find(sub[, start[, end]]), the brackets indicate optional arguments. So sub is required, but start is optional, and if you include start, then end is optional.

The in operator

The word in is a boolean operator that takes two strings and returns True if the first appears as a substring in the second:

'a' in team
'Boston' in team
# A. With well-chosen variable names, Python sometimes reads like English. 
#    Try to read this function before you execute it.
def in_both(word1, word2):
    for letter in word1:
        if letter in word2:
            print(letter)

Debugging

Let's go over function is_reverse together.

Exercise 04

The following functions are all intended to check whether a string contains any lowercase letters, but at least some of them are wrong. For each function, describe what the function actually does (assuming that the parameter is a string)

def any_lowercase1(s):
    for c in s:
        if c.islower():
            return True
        else:
            return False
        
def any_lowercase2(s):
    for c in s:
        if 'c'.islower():
            return 'True'
        else:
            return 'False'

def any_lowercase3(s):
    for c in s:
        flag = c.islower()
    return flag

def any_lowercase4(s):
    flag = False
    for c in s:
        flag = flag or c.islower()
    return flag

def any_lowercase5(s):
    for c in s:
        if not c.islower():
            return False
    return True

Exercise 05

A Caesar cypher is a weak form of encryption that involves “rotating” each letter by a fixed number of places. To rotate a letter means to shift it through the alphabet, wrapping around to the beginning if necessary, so ’A’ rotated by 3 is ’D’ and ’Z’ rotated by 1 is ’A’.

To rotate a word, rotate each letter by the same amount. For example, “cheer” rotated by 7 is “jolly” and “melon” rotated by -10 is “cubed”. In the movie 2001: A Space Odyssey, the ship computer is called HAL, which is IBM rotated by -1.

Write a function called rotate_word that takes a string and an integer as parameters, and returns a new string that contains the letters from the original string rotated by the given amount.

You might want to use the built-in function ord, which converts a character to a numeric code, and chr, which converts numeric codes to characters. Letters of the alphabet are encoded in alphabetical order, so for example:

ord('c') - ord('a')

Because 'c' is the two-eth letter of the alphabet. But beware: the numeric codes for upper case letters are different.

Potentially offensive jokes on the Internet are sometimes encoded in ROT13, which is a Caesar cypher with rotation 13. If you are not easily offended, find and decode some of them.

Then try to solve the level 1 of http://www.pythonchallenge.com.