Fork/Clone Ch.08 Lists/Strings Jedi Training
Fork/Clone Ch.08 Lists/Strings Jedi Training
The two new types of data introduced in this chapter are Lists and Tuples. Try running the following commands in the Python Console and see what is displayed:
type( (2, 3, 4, 5) )
type( [2, 3, 4, 5] )
The first type is a tuple and the second is a list. There are only two differences between a tuple and a list. First, it is created with parentheses rather than square brackets. Second, it is not possible to change the tuple once created. The term we use for that is immutable. Therefore lists are mutable and tuples are immutable. So why would we want to use a tuple if it is immutable? First, the computer can run faster if it knows the value won't change. Second, some lists we don't want to change, such as a list of RGB colors for red. The color red doesn't change, therefore an immutable tuple is a better choice.
Lists and Tuples can be assigned to a single variable name.
droids = ["R2D2","C3PO","BB8"]
print(droids)
['R2D2', 'C3PO', 'BB8']
How would I print out the second item in this list?
print(droids[1])
C3PO
This number with the item's location is called the index. Note that list locations start at zero. So a list with 10 elements does not have an element in location [10]. Just locations [0] through [9]. It can be very confusing to create an array of 10 items and then not have an item 10, but most computer languages start counting at 0 rather than 1.
Think of a list as an ice cube tray that holds numbers, as shown below. The values are stored inside each tray location, and written on the side of the tray are numbers starting at zero that identify the location of each holder. Don't mix the index and the value!
Remember, there are two sets of numbers to consider when working with a list of numbers: the position and the value. The position, also known as index, refers to where a value is. The value is the actual number stored at that location. When working with an array, make sure to think if you need the location or the value.
It is easy to get the value given the location, but it is harder to get the location given the value.
A program can assign new values to an individual element in a list. In the case below, the second spot at location one (not two) is changed to K2SO. Remember, you would not be able to do this if droids was a tuple instead of a list.
droids[1] = "K2SO"
print(droids)
['R2D2', 'K2SO', 'BB8']
Iteration is the process of stepping through each item in a collection of data such as a list. If a program needs to iterate through each item in a list, such as to print it out, there are two types of FOR loops that can do this.
The first method to iterate through each item in a loop is by using a "for-each" loop. This type of loop takes a collection of items, and loops the code once per item. It will take a copy of the item and store it in a variable for processing.
The format of the command:
for item_variable in list_name:
Here are some examples:
for robot in droids:
print(robot)
R2D2
K2SO
BB8
my_list = [3, 7, 5, 1, 0]
for item in my_list:
print(item)
3
7
5
1
0
my_list = [ [2,3], [4,3], [6,7] ]
for item in my_list:
print(item)
[2, 3]
[4, 3]
[6, 7]
You might have noticed in this last example that my_list is a list of lists!
You can still locate an item by its index number within the list's index number. Does that make sense? Hopefully the following code will demonstrate it clearly. The list nat_league_top3 contains a list of 3 lists and the lists would be indexed as 0,1,2. Each list then contains three teams which would also be indexed 0,1,2. In our example we look at nat_league_top3[1][0]. The [1] means the second list and the [0] means the first team in that second list, which just happens to be the greatest team in Major League Baseball, the Saint Louis Cardinals!
nat_league_top3 = [["Mets","Nationals","Braves"],
["Cardinals","Pirates","Cubs"],
["Dodgers","Giants","Diamondbacks"]]
team = nat_league_top3[1][0]
print(team)
Cardinals
The other way to iterate through a list is to use an index variable and directly access the list rather than through a copy of each item. To use an index variable, the program counts from 0 up to the length of the list. If there are ten elements, the loop must go from 0 to 9 for a total of ten elements.
my_list = [101, 20, 10, 50, 60]
for item in range(5):
print(my_list[item])
What if we added one more item to this list? Our code would no longer work correctly because the range would be different. The length of a list may be found by using the lenfunction. Combining that with the range function allows the program to loop through the entire list.
print(len(my_list))
5
Combine the two blocks of code above to give the following. This method is more complex, but is also more powerful. Because we are working directly with the list elements, rather than a copy, the list can be modified. The for-each loop does not allow modification of the original list.
my_list = [101, 20, 10, 50, 60]
for item in range(len(my_list)):
print(my_list[item])
New items may be added to a list (but not a tuple) by using the append command. For example:
my_list = [2, 4, 5, 6]
print(my_list)
my_list.append(9)
print(my_list)
[2, 4, 5, 6]
[2, 4, 5, 6, 9]
To create a list from scratch, it is necessary to create a blank list and then use the append function. This example creates a list based upon user input:
my_list = [] # Empty list
for i in range(5):
userInput = int(input( "Enter an integer: "))
my_list.append(userInput)
print(my_list)
Enter an integer: 2
[2]
Enter an integer: 4
[2, 4]
Enter an integer: 6
[2, 4, 6]
Enter an integer: 8
[2, 4, 6, 8]
Enter an integer: 8
[2, 4, 6, 8, 8]
If a program needs to create an array of a specific length, all with the same value, a simple trick is to use the following code:
# Creates a list with 3 of the same items.
my_list = ["nerf herder"] * 3
print(my_list)
['nerf herder', 'nerf herder',
'nerf herder']
Creating a running total of an array is a common operation. Here's how it is done:
my_list = [5, 76, 8, 5, 3, 3, 56, 5, 23]
list_total = 0
for item in my_list:
list_total += item
print(list_total)
184
Numbers in an array can also be changed by using a for loop. The following code doubles each item in the list.
my_list = [5, 76, 8, 5, 3, 3, 56, 5, 23]
for i in range(len(my_list)):
# Modify the element by doubling it
my_list[i] = my_list[i] * 2
print(my_list)
[10, 152, 16, 10, 6, 6, 112, 10, 46]
Strings are actually lists of characters. Strings are iterables. Iterables are sequences that can be counted in order, one at a time, during iteration. Strings contain a sequence of characters, one after another. The elements, including the spaces, are indexed, starting at 0. Let's setup a string and then check it's type for a review.
uhs_slogan = 'My school is the best'
type(uhs_slogan)
str
print(uhs_slogan)
My school is the best
Predict the output for each of the following lines of code in your notebook and then test them.
print(uhs_slogan[0])
print(uhs_slogan[2])
print(uhs_slogan[8])
print(uhs_slogan[-1])#from the right
print(uhs_slogan[26])
Python allows iterables such as lists and strings to be sliced. To slice, use square brackets and two indices separated by a colon. Python returns the iterable from the beginning index up to but not including the ending index.
print(uhs_slogan[0:5])
My sc
print(uhs_slogan[5:21])
hool is the best
When slicing, you can omit the starting (or ending) index if you want to start at the beginning (or end at the ending) of the string.
print(uhs_slogan[:6])
My sch
print(uhs_slogan[6:])
ool is the best
CHALLENGE: Try to print 'best' by slicing the variable uhs_slogan, omitting the end index.
Use slicing and concatenation to create your own sentence. Concatenation involves pasting together two strings, one after another. Follow the example here.
print(uhs_slogan[:13] + 'awesome!')
My school is awesome!
The len( ) function returns the number of elements in an iterable. The index of the last element is always one less than the length of the iterable since the indices begin at 0. Therefore since the length of this string is 21 characters long the index value of the last character "t" is index 20!
print(len(uhs_slogan))
21
Although this is rather strange, strings in Python may be used with some of the mathematical operators. Try the following code and see what Python does:
a = "Hi"
b = "There"
c = "!"
print(a + b)
print(a + b + c)
print(3 * a)
print(c * 4)
print((a * 2) + (b * 2))
Since a string is an array, a program can iterate through each character element just like an array:
for character in uhs_slogan:
print(character)
Here is a quick table for reference when doing text formatting. For a detailed explanation of how text formatting works, keep reading this incredibly exciting section!
Formatting Numbers
Try running the following program, which prints out several random numbers.
import random
for i in range(10):
x = random.randrange(120)
print("My number: ", x)
The output is left justified and numbers look terrible. Let's learn formatting:
import random
for i in range(10):
x = random.randrange(120)
print(f"My number: {x:3}")
This is better. We have right justified numbers! But how does it work? The first step is to type in an f, before the string. This stands for format, obviously. The string will not print out the curly braces { } but will instead replace them with the value in x. The :3 after the x, that we added isn't exactly obvious.
Here's the breakdown: The { } tells the computer we are going to format a number. After the : inside the curly braces will be formatting information. In this case we give it a 3 to specify a field width of three characters. The field width value tells the computer to try to fit the number into a field three characters wide. By default, it will try to right-justify numbers and left-justify text.
Even better, the program no longer needs to call str( ) to convert the number to a string. Leave the string conversions out. What if you had large numbers? Let's make bigger random numbers below.
import random
for i in range(10):
x = random.randrange(100000)
print(f"My number: {x:6,}")
We added a comma after the field width specifier, and now our numbers have commas. That comma must go after the field width specifier, not before. Commas are included in calculating the field width. For example, 1,024 has a field width of 5, not 4.
We can print multiple values, and combine the values with text. Run the following and examine the output.
x = 5
y = 66
z = 777
print(f"A - '{x}' B - '{y}' C - '{z}'")
The program will substitute numbers in for the curly braces, and still print out all of the other text in the string.
Formatting Strings
Let's look at how to format strings. Run the following and examine the output.
my_fruit = ["Apples","Oranges","Grapes","Pears"]
my_calories = [4, 300, 70, 30]
for i in range(4):
print(my_fruit[i], "are", my_calories[i], "calories.")
Now try it using the format command. Note how we can put additional text and more than one value into the same line. Remember text will be left justified and numbers are right justified.
my_fruit = ["Apples", "Oranges", "Grapes", "Pears"]
my_calories = [4, 300, 70, 30]
for i in range(4):
print(f"{my_fruit[i]:7} are {my_calories[i]:3} calories.")
That's outstanding, and it looks the way we want it. But what if we didn't want the numbers right justified, and the text left justified? We can use < and > characters like the following example...although that looks dumb.
my_fruit = ["Apples", "Oranges", "Grapes", "Pears"]
my_calories = [4, 300, 70, 30]
for i in range(4):
print(f"{my_fruit[i]:>7} are {my_calories[i]:<3} calories.")
Leading Zeros
for hours in range(9,13):
for minutes in range(5,12):
print(f"Time {hours}:{minutes}")
Run the above code and examine the output. We need to use leading zeros for displaying numbers in clocks. Rather than specify a 2 for the field width, instead use 02. This will pad the field with zeros rather than spaces.
for hours in range(9,13):
for minutes in range(5,12):
print(f"Time {hours:02}:{minutes:02}")
Floating Point Numbers
x = 0.1234
y = 123.456789
print(f"{x:.1} {y:.1}")
print(f"{x:.2} {y:.2}")
print(f"{x:.3} {y:.3}")
print(f"{x:.4} {y:.4}")
print(f"{x:.5} {y:.5}")
print(f"{x:.6} {y:.6}")
print()
print(f"{x:.1f} {y:.1f}")
print(f"{x:.2f} {y:.2f}")
print(f"{x:.3f} {y:.3f}")
print(f"{x:.4f} {y:.4f}")
print(f"{x:.5f} {y:.5f}")
print(f"{x:.6f} {y:.6f}")
Run the above code and examine the output. A format of .2 means to display the number with two digits of precision. Unfortunately this means if we display the number 123 which has three significant numbers rather than rounding it we get the number in scientific notation: 1.2e+02.
A format of .2f (note the f) means to display the number with two digits after the decimal point. So the number 1 would display as 1.00 and the number 1.5555 would display as 1.56.
A program can also specify a field width character. The format 10.2f does not mean 10 digits before the decimal and two after. It means a total field width of 10. So there will be 7 digits before the decimal, the decimal which counts as one more, and 2 digits after. Run the following and examine the output.
x = 0.1234
y = 123.456789
print(f"My number: '{x:10.1}' and '{y:10.1}'")
print(f"My number: '{x:10.2}' and '{y:10.2}'")
print(f"My number: '{x:10.3}' and '{y:10.3}'")
print(f"My number: '{x:10.4}' and '{y:10.4}'")
print(f"My number: '{x:10.5}' and '{y:10.5}'")
print(f"My number: '{x:10.6}' and '{y:10.6}'")
print()
print(f"My number: '{x:10.1f}' and '{y:10.1f}'")
print(f"My number: '{x:10.2f}' and '{y:10.2f}'")
print(f"My number: '{x:10.3f}' and '{y:10.3f}'")
print(f"My number: '{x:10.4f}' and '{y:10.4f}'")
print(f"My number: '{x:10.5f}' and '{y:10.5f}'")
print(f"My number: '{x:10.6f}' and '{y:10.6f}'")
Printing Dollars and Cents
cost = 3.07
tax = cost1 * 0.06
total = cost1 + tax1
print(f"Cost: ${cost:5.2f}")
print(f"Tax: {tax:5.2f}")
print(f"------------")
print(f"Total: ${total:5.2f}")
Remember! It would be easy to think that :5.2f would mean five digits, a decimal, followed by two digits. But it does not. It means a total field width of five, including the decimal and the two digits after.
As mentioned in the "Numbering System" chapter we can use Python to determine the decimal number of a character by using the ord function or find the character of a decimal number by using the chr function as seen below. What is the output of the following?
print(chr(64))
print(ord("!"))
We can take each character in a string, convert it to it's decimal value on the ASCII chart, do a math algorithm on it and then convert it back to characters. Below is a small, simple encryption program. Go through each line of code and try to determine what it is doing. If you run it, what does it output?
secret_message = "This is my secret message"
encrypted_text = ""
for c in secret_message:
x = ord(c)
x = x+1
c2 = chr(x)
encrypted_text = encrypted_text + c2
print(encrypted_text)
Challenge: Can you write a "decryption" program where you can enter the secret code output from the above program and convert it back to the original message?
One of the first and greatest text adventure games ever made was called Colossal Cave Adventure. One of your Jedi Tasks for this chapter is to make a game that will involve a list of rooms that can be navigated by going north, east, south, or west. Each room in the list will also be a list with the room description, and then what rooms are in each of the directions.
Before beginning you should draw a map and index each room of your adventure with a room number.
Below are the steps to let the user move around from room to room. You can make the game more exciting, however, by creating a collection of items that the user might find or can use to escape, conquer an enemy or find hidden treasure. This game can be as exciting and complicated as your talent allows. Print off an accompanying game map for the user to use. Make sure you fully comment your code.
Create an empty array called room_list.
Create a list variable called room. Set it equal to five elements. For the first element, create a string with a description of your first room. The last four elements will be the number of the next room if the user goes north, east, south, or west in that order. Look at your map to see what numbers to use. Use None if no room hooks up in that direction. (Do not put None in quotes. It is a special value that represents no value.)
Append this room to the room list.
Repeat the prior two steps for each room you want to create. Just re-use the room variable.
Create a variable called current_room. Set it to zero so that we will be in the first room in the room list.
Create a variable called done and set it to False. Create a while loop that repeats until done is set to True.
In the while loop print a blank line. This will make it visually separate each turn when playing the game.
Print the room description of the current room. Remember, if you are printing a list in a list the index goes after the first index. Don't do this: room_list[current_room[0]], do this: room_list[current_room][0]
After printing the room description, add a input line of code that asks the user what direction they wish to go: N,E,W,S or Q for quit and sets the answer to a variable.
Add an if statement to see if the user chose north.
If the user wanted to go north, create a variable called next_room and set it equal to room_list[current_room][1], which should be the number of the room to the north.
Add another if statement to see if the next room is equal to None. If it is, print "You can't go that way". ELSE set current_room equal to next_room. This essentially moves the user into the room.
Add elif statements to handle east, south, west and quit. Add a final else statement to let the user know the program doesn't understand what was typed if they typed something other than what they were supposed to.
If the user entered "q" or "quit", set done = True
Make sure that the program works for upper and lower case directions. Have the program work if the user types in "north" or "n".
Be creative and see how far you can expand this game.