by Al Sweigart
A tic-tac-toe board looks like a large hash symbol (#) with nine slots that can each contain an X, an O, or a blank. To represent the board with a dictionary, you can assign each slot a string-value key, as shown in Figure 5-2.
You can use string values to represent what’s in each slot on the board: 'X', 'O', or ' ' (a space character). Thus, you’ll need to store nine strings. You can use a dictionary of values for this. The string value with the key 'top-R' can represent the top-right corner, the string value with the key 'low-L' can represent the bottom-left corner, the string value with the key 'mid-M' can represent the middle, and so on.
Figure 5-2. The slots of a tic-tactoe board with their corresponding keys
This dictionary is a data structure that represents a tic-tac-toe board. Store this board-as-a-dictionary in a variable named theBoard. Open a new file editor window, and enter the following source code, saving it as ticTacToe.py:
theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ', 'mid-L': ' ', 'mid-M': ' ', 'mid-R': ' ', 'low-L': ' ', 'low-M': ' ', 'low-R': ' '}
The data structure stored in the theBoard variable represents the tic-tactoe board in Figure 5-3.
Figure 5-3. An empty tic-tac-toe board
Since the value for every key in theBoard is a single-space string, this dictionary represents a completely clear board. If player X went first and chose the middle space, you could represent that board with this dictionary:
theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ', 'mid-L': ' ', 'mid-M': 'X', 'mid-R': ' ', 'low-L': ' ', 'low-M': ' ', 'low-R': ' '}
The data structure in theBoard now represents the tic-tac-toe board in Figure 5-4.
Figure 5-4. The first move
A board where player O has won by placing Os across the top might look like this:
theBoard = {'top-L': 'O', 'top-M': 'O', 'top-R': 'O', 'mid-L': 'X', 'mid-M': 'X', 'mid-R': ' ', 'low-L': ' ', 'low-M': ' ', 'low-R': 'X'}
The data structure in theBoard now represents the tic-tac-toe board in Figure 5-5.
Figure 5-5. Player O wins.
Of course, the player sees only what is printed to the screen, not the contents of variables. Let’s create a function to print the board dictionary onto the screen. Make the following addition to ticTacToe.py (new code is in bold):
theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ', 'mid-L': ' ', 'mid-M': ' ', 'mid-R': ' ', 'low-L': ' ', 'low-M': ' ', 'low-R': ' '} def printBoard(board): print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R']) print('-+-+-') print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R']) print('-+-+-') print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R']) printBoard(theBoard)
When you run this program, printBoard() will print out a blank tic-tactoe board.
| | -+-+- | | -+-+- | |
The printBoard() function can handle any tic-tac-toe data structure you pass it. Try changing the code to the following:
theBoard = {'top-L': 'O', 'top-M': 'O', 'top-R': 'O', 'mid-L': 'X', 'mid-M': 'X', 'mid-R': ' ', 'low-L': ' ', 'low-M': ' ', 'low-R': 'X'} def printBoard(board): print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R']) print('-+-+-') print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R']) print('-+-+-') print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R']) printBoard(theBoard)
Now when you run this program, the new board will be printed to the screen.
O|O|O -+-+- X|X| -+-+- | |X
Because you created a data structure to represent a tic-tac-toe board and wrote code in printBoard() to interpret that data structure, you now have a program that “models” the tic-tac-toe board. You could have organized your data structure differently (for example, using keys like 'TOP-LEFT' instead of 'top-L'), but as long as the code works with your data structures, you will have a correctly working program.
For example, the printBoard() function expects the tic-tac-toe data structure to be a dictionary with keys for all nine slots. If the dictionary you passed was missing, say, the 'mid-L' key, your program would no longer work.
O|O|O -+-+- Traceback (most recent call last): File "ticTacToe.py", line 10, in
Now let’s add code that allows the players to enter their moves. Modify the ticTacToe.py program to look like this:
theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ', 'mid-L': ' ', 'mid-M': ' ', 'mid-R': ' ', 'low-L': ' ', 'low-M': ' ', 'low-R': ' '} def printBoard(board): print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R']) print('-+-+-') print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R']) print('-+-+-') print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R']) turn = 'X' for i in range(9): ➊ printBoard(theBoard) print('Turn for ' + turn + '. Move on which space?') ➋ move = input() ➌ theBoard[move] = turn ➍ if turn == 'X': turn = 'O' else: turn = 'X' printBoard(theBoard)
The new code prints out the board at the start of each new turn ➊, gets the active player’s move ➋, updates the game board accordingly ➌, and then swaps the active player ➍ before moving on to the next turn.
When you run this program, it will look something like this:
| | -+-+- | | -+-+- | | Turn for X. Move on which space? mid-M | | -+-+- |X| -+-+- | | Turn for O. Move on which space? low-L | | -+-+- |X| -+-+- O| | --snip-- O|O|X -+-+- X|X|O -+-+- O| |X Turn for X. Move on which space? low-M O|O|X -+-+- X|X|O -+-+- O|X|X
This isn’t a complete tic-tac-toe game—for instance, it doesn’t ever check whether a player has won—but it’s enough to see how data structures can be used in programs.
Note
If you are curious, the source code for a complete tic-tac-toe program is described in the resources available from http://nostarch.com/automatestuff/.
Nested Dictionaries and Lists
Modeling a tic-tac-toe board was fairly simple: The board needed only a single dictionary value with nine key-value pairs. As you model more complicated things, you may find you need dictionaries and lists that contain other dictionaries and lists. Lists are useful to contain an ordered series of values, and dictionaries are useful for associating keys with values. For example, here’s a program that uses a dictionary that contains other dictionaries in order to see who is bringing what to a picnic. The totalBrought() function can read this data structure and calculate the total number of an item being brought by all the guests.
allGuests = {'Alice': {'apples': 5, 'pretzels': 12}, 'Bob': {'ham sandwiches': 3, 'apples': 2}, 'Carol': {'cups': 3, 'apple pies': 1}} def totalBrought(guests, item): numBrought = 0 ➊ for k, v in guests.items(): ➋ numBrought = numBrought + v.get(item, 0) return numBrought print('Number of things being brought:') print(' - Apples ' + str(totalBrought(allGuests, 'apples'))) print(' - Cups ' + str(totalBrought(allGuests, 'cups'))) print(' - Cakes ' + str(totalBrought(allGuests, 'cakes'))) print(' - Ham Sandwiches ' + str(totalBrought(allGuests, 'ham sandwiches'))) print(' - Apple Pies ' + str(totalBrought(allGuests, 'apple pies')))
Inside the totalBrought() function, the for loop iterates over the key-value pairs in guests ➊. Inside the loop, the string of the guest’s name is assigned to k, and the dictionary of picnic items they’re bringing is assigned to v. If the item parameter exists as a key in this dictionary, it’s value (the quantity) is added to numBrought ➋. If it does not exist as a key, the get() method returns 0 to be added to numBrought.
The output of this program looks like this:
Number of things being brought: - Apples 7 - Cups 3 - Cakes 0 - Ham Sandwiches 3 - Apple Pies 1
This may seem like such a simple thing to model that you wouldn’t need to bother with writing a program to do it. But realize that this same totalBrought() function could easily handle a dictionary that contains thousands of guests, each bringing thousands of different picnic items. Then having this information in a data structure along with the totalBrought() function would save you a lot of time!
<
br /> You can model things with data structures in whatever way you like, as long as the rest of the code in your program can work with the data model correctly. When you first begin programming, don’t worry so much about the “right” way to model data. As you gain more experience, you may come up with more efficient models, but the important thing is that the data model works for your program’s needs.
Summary
You learned all about dictionaries in this chapter. Lists and dictionaries are values that can contain multiple values, including other lists and dictionaries. Dictionaries are useful because you can map one item (the key) to another (the value), as opposed to lists, which simply contain a series of values in order. Values inside a dictionary are accessed using square brackets just as with lists. Instead of an integer index, dictionaries can have keys of a variety of data types: integers, floats, strings, or tuples. By organizing a program’s values into data structures, you can create representations of real-world objects. You saw an example of this with a tic-tac-toe board.
Practice Questions
Q:
1. What does the code for an empty dictionary look like?
Q:
2. What does a dictionary value with a key 'foo' and a value 42 look like?
Q:
3. What is the main difference between a dictionary and a list?
Q:
4. What happens if you try to access spam['foo'] if spam is {'bar': 100}?
Q:
5. If a dictionary is stored in spam, what is the difference between the expressions 'cat' in spam and 'cat' in spam.keys()?
Q:
6. If a dictionary is stored in spam, what is the difference between the expressions 'cat' in spam and 'cat' in spam.values()?
Q:
7. What is a shortcut for the following code?
if 'color' not in spam: spam['color'] = 'black'
Q:
8. What module and function can be used to “pretty print” dictionary values?
Practice Projects
For practice, write programs to do the following tasks.
Fantasy Game Inventory
You are creating a fantasy video game. The data structure to model the player’s inventory will be a dictionary where the keys are string values describing the item in the inventory and the value is an integer value detailing how many of that item the player has. For example, the dictionary value {'rope': 1, 'torch': 6, 'gold coin': 42, 'dagger': 1, 'arrow': 12} means the player has 1 rope, 6 torches, 42 gold coins, and so on.
Write a function named displayInventory() that would take any possible “inventory” and display it like the following:
Inventory: 12 arrow 42 gold coin 1 rope 6 torch 1 dagger Total number of items: 62
Hint: You can use a for loop to loop through all the keys in a dictionary.
# inventory.py stuff = {'rope': 1, 'torch': 6, 'gold coin': 42, 'dagger': 1, 'arrow': 12} def displayInventory(inventory): print("Inventory:") item_total = 0 for k, v in inventory.items(): # FILL THIS PART IN print("Total number of items: " + str(item_total)) displayInventory(stuff)
List to Dictionary Function for Fantasy Game Inventory
Imagine that a vanquished dragon’s loot is represented as a list of strings like this:
dragonLoot = ['gold coin', 'dagger', 'gold coin', 'gold coin', 'ruby']
Write a function named addToInventory(inventory, addedItems), where the inventory parameter is a dictionary representing the player’s inventory (like in the previous project) and the addedItems parameter is a list like dragonLoot. The addToInventory() function should return a dictionary that represents the updated inventory. Note that the addedItems list can contain multiples of the same item. Your code could look something like this:
def addToInventory(inventory, addedItems): # your code goes here inv = {'gold coin': 42, 'rope': 1} dragonLoot = ['gold coin', 'dagger', 'gold coin', 'gold coin', 'ruby'] inv = addToInventory(inv, dragonLoot) displayInventory(inv)
The previous program (with your displayInventory() function from the previous project) would output the following:
Inventory: 45 gold coin 1 rope 1 ruby 1 dagger Total number of items: 48
Chapter 6. Manipulating Strings
Text is one of the most common forms of data your programs will handle. You already know how to concatenate two string values together with the + operator, but you can do much more than that. You can extract partial strings from string values, add or remove spacing, convert letters to lowercase or uppercase, and check that strings are formatted correctly. You can even write Python code to access the clipboard for copying and pasting text.
In this chapter, you’ll learn all this and more. Then you’ll work through two different programming projects: a simple password manager and a program to automate the boring chore of formatting pieces of text.
Working with Strings
Let’s look at some of the ways Python lets you write, print, and access strings in your code.
String Literals
Typing string values in Python code is fairly straightforward: They begin and end with a single quote. But then how can you use a quote inside a string? Typing 'That is Alice's cat.' won’t work, because Python thinks the string ends after Alice, and the rest (s cat.') is invalid Python code. Fortunately, there are multiple ways to type strings.
Double Quotes
Strings can begin and end with double quotes, just as they do with single quotes. One benefit of using double quotes is that the string can have a single quote character in it. Enter the following into the interactive shell:
>>> spam = "That is Alice's cat."
Since the string begins with a double quote, Python knows that the single quote is part of the string and not marking the end of the string. However, if you need to use both single quotes and double quotes in the string, you’ll need to use escape characters.
Escape Characters
An escape character lets you use characters that are otherwise impossible to put into a string. An escape character consists of a backslash () followed by the character you want to add to the string. (Despite consisting of two characters, it is commonly referred to as a singular escape character.) For example, the escape character for a single quote is '. You can use this inside a string that begins and ends with single quotes. To see how escape characters work, enter the following into the interactive shell:
>>> spam = 'Say hi to Bob's mother.'
Python knows that since the single quote in Bob's has a backslash, it is not a single quote meant to end the string value. The escape characters ' and " let you put single quotes and double quotes inside your strings, respectively.
Table 6-1 lists the escape characters you can use.
Table 6-1. Escape Characters
Escape character
Prints as
'
Single quote
"
Double quote
t
Tab
n
Newline (line break)
\
Backslash
Enter the following into the interactive shell:
>>> print("Hello there!nHow are you?nI'm doing fine.") Hello there! How are you? I'm doing fine.
Raw Strings
You can place an r before the beginning quotation mark of a string to make it a raw string. A raw string completely ignores all escape characters and prints any backslash that appears in the string. For example, type the following into the interactive shell:
>>> print(r'That is Carol's cat.') That is Carol's cat.
Because this is a raw string, Python considers the backslash as part of the string and not as the start of an escape character. Raw strings are helpful if you are typing string values that contain many backslashes, such as the strings used for regular expressions described in the next chapter.
Multiline Strings with Triple Quotes
While you can use the n escape character to put a newline into a string, it is often easier to use multil
ine strings. A multiline string in Python begins and ends with either three single quotes or three double quotes. Any quotes, tabs, or newlines in between the “triple quotes” are considered part of the string. Python’s indentation rules for blocks do not apply to lines inside a multiline string.
Open the file editor and write the following:
print('''Dear Alice, Eve's cat has been arrested for catnapping, cat burglary, and extortion. Sincerely, Bob''')
Save this program as catnapping.py and run it. The output will look like this:
Dear Alice, Eve's cat has been arrested for catnapping, cat burglary, and extortion. Sincerely, Bob
Notice that the single quote character in Eve's does not need to be escaped. Escaping single and double quotes is optional in multiline strings. The following print() call would print identical text but doesn’t use a multiline string:
print('Dear Alice,nnEve's cat has been arrested for catnapping, cat burglary, and extortion.nnSincerely,nBob')
Multiline Comments