In this article by Daniel Bates, the author of Raspberry Pi for Kids - Second edition, we're going to learn and use the Python programming language to generate random funny phrases such as, Alice has a smelly foot!
(For more resources related to this topic, see here.)
Python
In this article, we are going to use the Python programming language. Almost all programming languages are capable of doing the same things, but they are usually designed with different specializations. Some languages are designed to perform one job particularly well, some are designed to run code as fast as possible, and some are designed to be easy to learn.
Scratch was designed to develop animations and games, and to be easy to read and learn, but it can be difficult to manage large programs. Python is designed to be a good general-purpose language. It is easy to read and can run code much faster than Scratch.
Python is a text-based language. Using it, we type the code rather than arrange building blocks. This makes it easier to go back and change the pieces of code that we have already written, and it allows us to write complex pieces of code more quickly. It does mean that we need to type our programs accurately, though—there are no limits to what we can type, but not all text will form a valid program. Even a simple spelling mistake can result in errors. Lots of tutorials and information about the available features are provided online at http://docs.python.org/2/. Learn Python the Hard Way, by Shaw Zed A., is another good learning resource, which is available at http://learnpythonthehardway.org.
As an example, let's take a look at some Scratch and Python code, respectively, both of which do the same thing. Here's the Scratch code:
The Python code that does the same job looks like:
def count(maximum):
value = 0
while value < maximum:
value = value + 1
print "value =", value
count(5)
Even if you've never seen any Python code before, you might be able to read it and tell what it does. Both the Scratch and Python code count from 0 to a maximum value, and display the value each time.
The biggest difference is in the first line. Instead of waiting for a message, we define (or create) a function, and instead of sending a message, we call the function (more on how to run Python code, shortly). Notice that we include maximum as an argument to the count function. This tells Python the particular value we would like to keep as the maximum, so we can use the same code with different maximum values.
The other main differences are that we have while instead of forever if, and we have print instead of say. These are just different ways of writing the same thing. Also, instead of having a block of code wrap around other blocks, we simply put an extra four spaces at the beginning of a line to show which code is contained within a particular block.
Python programming
To run a piece of Python code, open Python 2 from the Programming menu on the Raspberry Pi desktop and perform the following steps:
Type the previous code into the window and you should notice that it can recognize how many spaces to start a line with.
When you have finished the function block, press Enter a couple of times, until you see >>>. This shows that Python recognizes that your block of code has been completed, and that it is ready to receive a new command.
Now, you can run your code by typing in count(5) and pressing Enter. You can change 5 to any number you like and press Enter again to count to a different number.
We're now ready to create our program!
The Raspberry Pi also supports Python 3, which is very similar but incompatible with Python 2. You can check out the differences between Python 2 and Python 3 at http://python-future.org/compatible_idioms.html.
The program we're going to use to generate phrases
As mentioned earlier, our program is going to generate random, possibly funny, phrases for us. To do this, we're going to give each phrase a common structure, and randomize the word that appears in each position. Each phrase will look like:
<name> has a <adjective> <noun>
Where <name> is replaced by a person's name, <adjective> is replaced by a descriptive word, and <noun> is replaced by the name of an object.
This program is going to be a little larger than our previous code example, so we're going to want to save it and modify it easily. Navigate to File | New Window in Python 2. A second window will appear which starts off completely blank. We will write our code in this window, and when we run it, the results will appear in the first window. For the rest of the article, I will call the first window the Shell, and the new window the Code Editor. Remember to save your code regularly!
Lists
We're going to use a few different lists in our program. Lists are an important part of Python, and allow us to group together similar things. In our program, we want to have separate lists for all the possible names, adjectives, and nouns that can be used in our sentences. We can create a list in this manner:
names = ["Alice", "Bob", "Carol"]
Here, we have created a variable called names, which is a list. The list holds three items or elements: Alice, Bob, and Carol. We know that it is a list because the elements are surrounded by square brackets, and are separated by commas. The names need to be in quote marks to show that they are text, and not the names of variables elsewhere in the program.
To access the elements in a list, we use the number which matches its position, but curiously, we start counting from zero. This is because if we know where the start of the list is stored, we know that its first element is stored at position start + 0, the second element is at position start + 1, and so on. So, Alice is at position 0 in the list, Bob is at position 1, and Carol is at position 2. We use the following code to display the first element (Alice) on the screen:
print names[0]
We've seen print before: it displays text on the screen. The rest of the code is the name of our list (names), and the position of the element in the list that we want surrounded by square brackets.
Type these two lines of code into the Code Editor, and then navigate to Run | Run Module (or press F5). You should see Alice appear in the Shell. Feel free to play around with the names in the list or the position that is being accessed until you are comfortable with how lists work. You will need to rerun the code after each change. What happens if you choose a position that doesn't match any element in the list, such as 10?
Adding randomness
So far, we have complete control over which name is displayed. Let's now work on displaying a random name each time we run the program. Update your code in the Code Editor so it looks like:
import random
names = ["Alice", "Bob", "Carol"]
position = random.randrange(3)
print names[position]
In the first line of the code, we import the random module. Python comes with a huge amount of code that other people have written for us, separated into different modules. Some of this code is simple, but makes life more convenient for us, and some of it is complex, allowing us to reuse other people's solutions for the challenges we face and concentrate on exactly what we want to do. In this case, we are making use of a collection of functions that deal with random behavior. We must import a module before we are able to access its contents.
Information on the available modules available can be found online at www.python.org/doc/.
After we've created the list of names, we then compute a random position in the list to access. The name random.randrange tells us that we are using a function called randrange, which can be found inside the random module that we imported earlier. The randrange function gives us a random whole number less than the number we provide. In this case, we provide 3 because the list has three elements and we store the random position in a new variable called position. Finally, instead of accessing a fixed element in the names list, we access the element that position refers to.
If you run this code a few times, you should notice that different names are chosen randomly.
Now, what happens if we want to add a fourth name, Dave, to our list? We need to update the list itself, but we also need to update the value we provide to randrange to let it know that it can give us larger numbers. Making multiple changes just to add one name can cause problems—if the program is much larger, we may forget which parts of the code need to be updated. Luckily, Python has a nice feature which allows us to make this simpler.
Instead of a fixed number (such as 3), we can ask Python for the length of a list, and provide that to the randrange function. Then, whenever we update the list, Python knows exactly how long it is, and can generate suitable random numbers. Here is the code, which is updated to make it easier to change the length of the list:
import random
names = ["Alice", "Bob", "Carol"]
length = len(names)
position = random.randrange(length)
print names[position]
Here, we've created a new variable called length to hold the length of the list. We then use the len function (which is short for length) to compute the length of our list, and we give length to the randrange function. If you run this code, you should see that it works exactly as it did before, and it easily copes if you add or remove elements from the list.
It turns out that this is such a common thing to do, that the writers of the random module have provided a function which does the same job. We can use this to simplify our code:
import random
names = ["Alice", "Bob", "Carol", "Dave"]
print random.choice(names)
As you can see, we no longer need to compute the length of the list or a random position in it: random.choice does all of this for us, and simply gives us a random element of any list we provide it with. As we will see in the next section, this is useful since we can reuse random.choice for all the different lists we want to include in our program.
If you run this program, you will see that it works the same as it did before, despite being much shorter.
Creating phrases
Now that we can get a random element from a list, we've crossed the halfway mark to generating random sentences!
Create two more lists in your program, one called adjectives, and the other called nouns. Put as many descriptive words as you like into the first one, and a selection of objects into the second. Here are the three lists I now have in my program:
names = ["Alice", "Bob", "Carol", "Dave"]
adjectives = ["fast", "slow", "pretty", "smelly"]
nouns = ["dog", "car", "face", "foot"]
Also, instead of printing our random elements immediately, let's store them in variables so that we can put them all together at the end. Remove the existing line of code with print in it, and add the following three lines after the lists have been created:
name = random.choice(names)
adjective = random.choice(adjectives)
noun = random.choice(nouns)
Now, we just need to put everything together to create a sentence. Add this line of code right at the end of the program:
print name, "has a", adjective, noun
Here, we've used commas to separate all of the things we want to display. The name, adjective, and noun are our variables holding the random elements of each of the lists, and "has a" is some extra text that completes the sentence. print will automatically put a space between each thing it displays (and start a new line at the end). If you ever want to prevent Python from adding a space between two items, separate them with + rather than a comma.
That's it! If you run the program, you should see random phrases being displayed each time, such as Alice has a smelly foot or Carol has a fast car.
Making mischief
So, we have random phrases being displayed, but what if we now want to make them less random? What if you want to show your program to a friend, but make sure that it only ever says nice things about you, or bad things about them? In this section, we'll extend the program to do just that.
Dictionaries
The first thing we're going to do is replace one of our lists with a dictionary. A dictionary in Python uses one piece of information (a number, some text, or almost anything else) to search for another. This is a lot like the dictionaries you might be used to, where you use a word to search for its meaning. In Python, we say that we use a key to look for a value.
We're going to turn our adjectives list into a dictionary. The keys will be the existing descriptive words, and the values will be tags that tell us what sort of descriptive words they are. Each adjective will be "good" or "bad".
My adjectives list becomes the following dictionary. Make similar changes to yours.
adjectives = {"fast":"good", "slow":"bad", "pretty":"good", "smelly":"bad"}
As you can see, the square brackets from the list become curly braces when you create a dictionary. The elements are still separated by commas, but now each element is a key-value pair with the adjective first, then a colon, and then the type of adjective it is.
To access a value in a dictionary, we no longer use the number which matches its position. Instead, we use the key with which it is paired. So, as an example, the following code will display "good" because "pretty" is paired with "good" in the adjectives dictionary:
print adjectives["pretty"]
If you try to run your program now, you'll get an error which mentions random.choice(adjectives). This is because random.choice expects to be given a list, but is now being given a dictionary. To get the code working as it was before, replace that line of code with this:
adjective = random.choice(adjectives.keys())
The addition of .keys() means that we only look at the keys in the dictionary—these are the adjectives we were using before, so the code should work as it did previously. Test it out now to make sure.
Loops
You may remember the forever and repeat code blocks in Scratch. In this section, we're going to use Python's versions of these to repeatedly choose random items from our dictionary until we find one which is tagged as "good". A loop is the general programming term for this repetition—if you walk around a loop, you will repeat the same path over and over again, and it is the same with loops in programming languages.
Here is some code, which finds an adjective and is tagged as "good". Replace your existing adjective = line of code with these lines:
while True:
adjective = random.choice(adjectives.keys())
if adjectives[adjective] == "good":
break
The first line creates our loop. It contains the while key word, and a test to see whether the code should be executed inside the loop. In this case, we make the test True, so it always passes, and we always execute the code inside. We end the line with a colon to show that this is the beginning of a block of code. While in Scratch we could drag code blocks inside of the forever or repeat blocks, in Python we need to show which code is inside the block in a different way. First, we put a colon at the end of the line, and then we indent any code which we want to repeat by four spaces.
The second line is the code we had before: we choose a random adjective from our dictionary.
The third line uses adjectives[adjective] to look into the (adjectives) dictionary for the tag of our chosen adjective. We compare the tag with "good" using the double = sign (a double = is needed to make the comparison different from the single = case, which stores a value in a variable). Finally, if the tag matches "good",we enter another block of code: we put a colon at the end of the line, and the following code is indented by another four spaces. This behaves the same way as the Scratch if block.
The fourth line contains a single word: break. This is used to escape from loops, which is what we want to do now that we have found a "good" adjective.
If you run your code a few times now, you should see that none of the bad adjectives ever appear.
Conditionals
In the preceding section, we saw a simple use of the if statement to control when some code was executed. Now, we're going to do something a little more complex. Let's say we want to give Alice a good adjective, but give Bob a bad adjective. For everyone else, we don't mind if their adjective is good or bad.
The code we already have to choose an adjective is perfect for Alice: we always want a good adjective. We just need to make sure that it only runs if our random phrase generator has chosen Alice as its random person. To do this, we need to put all the code for choosing an adjective within another if statement, as shown here:
if name == "Alice":
while True:
adjective = random.choice(adjectives.keys())
if adjectives[adjective] == "good":
break
Remember to indent everything inside the if statement by an extra four spaces.
Next, we want a very similar piece of code for Bob, but also want to make sure that the adjective is bad:
elif name == "Bob":
while True:
adjective = random.choice(adjectives.keys())
if adjectives[adjective] == "bad":
break
The only differences between this and Alice's code is that the name has changed to "Bob", the target tag has changed to "bad", and if has changed to elif. The word elif in the code is short for else if. We use this version because we only want to do this test if the first test (with Alice) fails. This makes a lot of sense if we look at the code as a whole: if our random person is Alice, do something, else if our random person is Bob, do something else.
Finally, we want some code that can deal with everyone else. This time, we don't want to perform another test, so we don't need an if statement: we can just use else:
else:
adjective = random.choice(adjectives.keys())
With this, our program does everything we wanted it to do. It generates random phrases, and we can even customize what sort of phrase each person gets. You can add as many extra elif blocks to your program as you like, so as to customize it for different people.
Functions
In this section, we're not going to change the behavior of our program at all; we're just going to tidy it up a bit.
You may have noticed that when customizing the types of adjectives for different people, you created multiple sections of code, which were almost identical. This isn't a very good way of programming because if we ever want to change the way we choose adjectives, we will have to do it multiple times, and this makes it much easier to make mistakes or forget to make a change somewhere.
What we want is a single piece of code, which does the job we want it to do, and then be able to use it multiple times. We call this piece of code a function. We saw an example of a function being created in the comparison with Scratch at the beginning of this article, and we've used a few functions from the random module already. A function can take some inputs (called arguments) and does some computation with them to produce a result, which it returns.
Here is a function which chooses an adjective for us with a given tag:
def chooseAdjective(tag):
while True:
item = random.choice(adjectives.keys())
if adjectives[item] == tag:
break
return item
In the first line, we use def to say that we are defining a new function. We also give the function's name and the names of its arguments in brackets. We separate the arguments by commas if there is more than one of them. At the end of the line, we have a colon to show that we are entering a new code block, and the rest of the code in the function is indented by four spaces.
The next four lines should look very familiar to you—they are almost identical to the code we had before. The only difference is that instead of comparing with "good" or "bad", we compare with the tag argument. When we use this function, we will set tag to an appropriate value.
The final line returns the suitable adjective we've found. Pay attention to its indentation. The line of code is inside the function, but not inside the while loop (we don't want to return every item we check), so it is only indented by four spaces in total.
Type the code for this function anywhere above the existing code, which chooses the adjective; the function needs to exist in the code prior to the place where we use it. In particular, in Python, we tend to place our code in the following order:
Imports
Functions
Variables
Rest of the code
This allows us to use our functions when creating the variables. So, place your function just after the import statement, but before the lists. We can now use this function instead of the several lines of code that we were using before. The code I'm going to use to choose the adjective now becomes:
if name == "Alice":
adjective = chooseAdjective("good")
elif name == "Bob":
adjective = chooseAdjective("bad")
else:
adjective = random.choice(adjectives.keys())
This looks much neater! Now, if we ever want to change how an adjective is chosen, we just need to change the chooseAdjective function, and the change will be seen in every part of the code where the function is used.
Complete code listing
Here is the final code you should have when you have completed this article. You can use this code listing to check that you have everything in the right order, or look for other problems in your code. Of course, you are free to change the contents of the lists and dictionaries to whatever you like; this is only an example:
import random
def chooseAdjective(tag):
while True:
item = random.choice(adjectives.keys())
if adjectives[item] == tag:
break
return item
names = ["Alice", "Bob", "Carol", "Dave"]
adjectives = {"fast":"good", "slow":"bad", "pretty":"good", "smelly":"bad"}
nouns = ["dog", "car", "face", "foot"]
name = random.choice(names)
#adjective = random.choice(adjectives)
noun = random.choice(nouns)
if name == "Alice":
adjective = chooseAdjective("good")
elif name == "Bob":
adjective = chooseAdjective("bad")
else:
adjective = random.choice(adjectives.keys())
print name, "has a", adjective, noun
Summary
In this article, we learned about the Python programming language and how it can be used to create random phrases. We saw that it shared lots of features with Scratch, but is simply presented differently.
Resources for Article:
Further resources on this subject:
Develop a Digital Clock [article]
GPS-enabled Time-lapse Recorder [article]
The Raspberry Pi and Raspbian [article]
Read more