PROJECT 8
In this project you’re going to put together a program that spits out a hilarious mixture of words — sort of like Mad Libs. To do this, you’re going to tackle string formatting for prettier printing.
This project’s object is to create templates for words that are randomly chosen from a list. The randomness of the words makes them absurd and, hopefully, funny enough to make you laugh out loud.
To complete this project, you
Format strings let you fit changeable bits of a message into a template. That way it’s easier to present your messages, and it makes your code more readable.
Fire up IDLE. At the interpreter, type the following and be careful — there are two strings. The first is "Hi there %s. You are such a good author." and the second is "Brendan". In between them is the % character.
>>> print("Hi there %s. You are such a good author."%"Brendan")
Hi there Brendan. You are such a good author.
Why, thank you. That’s a very kind thing to say.
Can you see what has happened there? The second string ("Brendan") was substituted into the first string in the place where the %s was.
The specifier in this case (%s) says to convert the formatting value into a string. (s stands for string.) The main specifier you’ll want is %s (that is, convert to a string). A lot of other specifiers exist, but you’ll mainly work with these: integer %i, fixed point %f, general %g, and percent sign %.
To make a silly sentence, you need to mix up a heap of words into a message template. Substitute more than just one word into a template. To do that, follow these steps:
A quick example:
>>> "%s %s"%(1,2)
'1 2'
You get an error if the number of specifiers doesn’t match the number of values to be formatted. Try these:
"%s %s"%(1) (two specifiers, one value)
"%s %s"%(1,2,3) (two specifiers, three values)
Pay attention to the errors; sooner or later you’ll need them for debugging. You can pre-save your values before you pass them to your format string:
>>> values = (1,2)
>>> "%s %s"%values
'1 2'
>>> # Snuck in a tuple:
>>> type(values)
<type 'tuple'>
The formatting operator takes only one argument. When you place parentheses around a number of items separated by commas, you actually get a new object called a tuple. It is this single object that is passed to the formatting operator.
You’ll have a couple of buckets of words for the silly sentences. Choose one word from each bucket and put them together. The formatting operator only takes one argument, so pack all the words into an object so you can pass it, as a single argument, to the formatting operator. For that you need a tuple.
Tuples are the most misunderstood Python data type. They’re a bit like lists. Some people think tuples are worse than lists, but they’re just different. You can get the values of the elements of a tuple, but you can’t change them. Tuples are immutable, which is a fancy way of saying you can’t change them.
Tuples are good for
You can iterate (go) through tuples like you would a list and can read individual elements like a list:
>>> my_tuple = ('e','3')
>>> for e in my_tuple:
print(e)
e
3
>>> my_tuple[0]
'e'
>>> my_tuple[1]
'3'
>>> my_tuple(0) # use [] not ()!
Traceback (most recent call last):
File "<pyshell#42>", line 1, in <module>
my_tuple(0) # [] not ()!
TypeError: 'tuple' object is not callable
Here, my_tuple[0] retrieves the value of the first element (elements are numbered from 0) in the tuple. It has the same square bracket notation that lists have. The number of the elements starts from 0, just like the substitutions were in the 133t sp34k3r project.
Confirm that you can’t change the element by trying to assign a value to element 0: my_tuple[0] = 17. Be familiar with the error (it will tell you that tuples don’t support item assignment) so you’ll recognize it when you see it later.
You can have a tuple with only one element. Why? Because the code you want to use might expect a tuple. How would you make a tuple with only one element? The answer is to proceed as normal, putting parentheses around your one element, but to add a comma after it.
>>> my_one_element_tuple = (1,)
>>> my_one_element_tuple
(1,)
The comma tells Python that it’s a tuple; technically it’s the comma, not the parentheses, that’s doing the work. You usually need to use parentheses because the syntax is hard to figure out.
Tuples let you return more than one value from a function, which is handy when you want to return more than one value from a function. But the code calling the function can treat the values as a single tuple or as individual elements. This is called unpacking the tuple.
You have to give the tuple exactly the right number of elements if you want the values unpacked. For example, here the test_function returns a tuple with three values (1,2,3). The code can either receive it as a single object a= test_function() or as three separate objects in a,b,c = test_function(), but not as any other number.
You can use this approach when you want to return more than one value from a function:
>>> def test_function():
return (1,2,3) # returns a tuple with three elements
>>> a = test_function()
>>> a
(1, 2, 3)
>>> a,b,c = test_function()
>>> a
1
>>> b
2
>>> c
3
>>> a,b = test_function()
Traceback (most recent call last):
File "<pyshell#59>", line 1, in <module>
a,b = test_function()
ValueError: too many values to unpack
You can also unpack a tuple directly:
>>> a,b,c = (1,2,3) # unpack the tuple into a, b, c
>>> print("a: %s, b: %s, c: %s"%(a,b,c))
a: 1, b: 2, c: 3
>>> a,b = (1,2,3) # three values but only two variables.
Traceback (most recent call last):
File "<pyshell#62>", line 1, in <module>
a,b = (1,2,3)
ValueError: too many values to unpack
To make silly sentences, you need a template where you can plug in some words. Think of this like:
<Person or animal> <verbed> the <adjective> <noun>.
(Yeah, yeah. I know verbed isn’t a word.) A formatting template for this would look like what you see here. Each %s is a conversion specifier and marks a place where a value will be inserted into the string:
template = "%s %s the %s %s."
Time to get started on your silly sentences:
"""silly_sentencer.py
This program prints silly sentences by mapping random words
into a formatting template
Brendan Scott
Jan 2015
"""
template = "%s %s the %s %s."
Store the following sample in a constant called BASE_SENTENCE.
You’ll look at this sentence when you’re testing your code.
BASE_SENTENCE = "My Python teacher wrote the Python book."
persons = ["My Python teacher"]
verbs = ["wrote"]
adjectives = ["Python"]
nouns = ["book"]
When you’re first working on a project, start simple. Make it more complex later. Don’t try to conquer the universe in your first go. That way, miMMisMistakes are easier to find and fix.
You should have something that looks like this:
"""silly_sentencer.py
This program prints silly sentences by mapping random words
into a formatting template
Brendan Scott
Jan 2015
"""
BASE_SENTENCE = "My Python teacher wrote the Python book."
template = "%s %s the %s %s."
persons = ["My Python teacher"]
verbs = ["wrote"]
adjectives = ["Python"]
nouns = ["book"]
Now test to make sure that applying the values from the lists to the template gives you the base sentence.
Add the following code and run the file.
This code creates a tuple from the first element of each list, then passes that tuple into the formatting string. The last print statement compares whether the formatted string is the same as what you started with:
# Main Section
if __name__ == "__main__":
person = persons[0]
verb = verbs[0]
adjective = adjectives[0]
noun = nouns[0]
format_values = (person, verb, adjective, noun)
print(BASE_SENTENCE)
print(template%format_values)
print(BASE_SENTENCE == template%format_values)
You should get this:
>>> ================================ RESTART ================================
>>>
My Python teacher wrote the Python book.
My Python teacher wrote the Python book.
True
To fill in the template, choose an element from each list at random. Because there’s only one element in each list at the moment, it’s not all that random. However, you’re testing with one element and then adding other elements later. When you have that, you can use the code you already have to populate the template with the words you’ve chosen.
Choosing at random from a list is relatively easy. Use the choice function from the random module. In this example I use a list comprehension to make a list with the numbers from 0 through 9, then I use random.choice to choose one of them at random:
>>> import random
>>> sample_list = [x for x in range(10)]
>>> random.choice(sample_list)
1
Now bring it all together in your code:
Print out the base sentence for comparison and the template formatted with the format values.
Here’s the code:
"""silly_sentencer.py
This program prints silly sentences by mapping random words
into a formatting template
Brendan Scott
Jan 2015
"""
# Imports Section
import random
# Constants Section
BASE_SENTENCE= "My Python teacher wrote the Python book."
template = "%s %s the %s %s."
persons = ["My Python teacher"]
verbs = ["wrote"]
adjectives = ["Python"]
nouns = ["book"]
# Main Section
if __name__ == "__main__":
person = random.choice(persons)
verb = random.choice(verbs)
adjective = random.choice(adjectives)
noun = random.choice(nouns)
format_values = (person, verb, adjective, noun)
print(BASE_SENTENCE)
print(template%format_values)
Run it!
You’re testing that the function works properly.
>>> ================================ RESTART ================================
>>>
My Python teacher wrote the Python book.
My Python teacher wrote the Python book.
This code looks like it’s working right. The first line is the original string you started with. The second is the string you created using the formatting template.
Now you can get creative. For each list think of other words which you could put in there. You haven’t tested your code with a list longer than one yet, so just add words to a single list, not all of them at once.
The other words that you’re choosing:
Here are some extra words to get you started:
persons = ["My Python teacher", "Dinsdale Piranha", "Tim", "Mrs Pepperpot", "My dad", "The Hungarian"]
verbs = ["wrote", "sneezed", "looked at", "drove", "made", "stole"]
adjectives = ["Python", "slippery", "funniest", "big", "smelly", "poky", "silly"]
nouns = ["book", "eels", "hovercraft", "nose", "shoes", "joke", "walk"]
You can split a long list
over multiple lines. (Like that.)
You can add a new line after any comma.
When you’ve got all the elements you want, finish with a closing square bracket (and no comma). Run the code a few times to see if it works. (I removed the restart lines from this printout.)
Mrs Pepperpot looked at the big eels.
My dad sneezed the funniest shoes.
Dinsdale Piranha wrote the Python book.
Dinsdale Piranha wrote the silly nose.
Tim made the Python shoes.
My dad made the Python walk.
My Python teacher drove the slippery joke.
Tim wrote the Python shoes.
Everything seems to be working. This function is a little hard to test because the output is random (unlike addition, where you know what the answer should be).
When you’re satisfied, you can finish filling out your word lists. If you like, you can also put the whole section into a for loop to print multiple sentences each time you run the program.
If you add those changes to the previous code, you get your final code. It looks like this:
"""silly_sentencer.py
This program prints silly sentences by mapping random words
into a formatting template
Brendan Scott
Jan 2015
"""
# Imports Section
import random
# Constants Section
BASE_SENTENCE = "My Python teacher wrote the Python book."
template = "%s %s the %s %s."
persons = ["My Python teacher", "Dinsdale Piranha", "Tim", "Mrs Pepperpot", "My dad", "The Hungarian"]
verbs = ["wrote", "sneezed", "looked at", "drove", "made", "stole"]
adjectives = ["Python", "slippery", "funniest", "big", "smelly", "poky", "silly"]
nouns = ["book", "eels", "hovercraft", "nose", "shoes", "joke", "walk"]
# Main Section
if __name__ == "__main__":
person = random.choice(persons)
verb = random.choice(verbs)
adjective = random.choice(adjectives)
noun = random.choice(nouns)
format_values = (person, verb, adjective, noun)
## print(BASE_SENTENCE)
print(template%format_values)
In this project you used Python's formatting operator % to produce silly sentences. Along the way you saw:
3.133.119.66