Chapter 10: Functions

Let’s write a program to see if you and your mate are compatible. We’ll ask for your zodiac sign, your mate’s sign, and see what astrology predicts about each of you. (We’ll leave it to you to interpret the results.)

Let’s start by asking for your sign:

print "What's your sign?"
your_sign = input()

Note that the variable your_sign has an underscore character to separate the two words. So far all of our variables have been a single English word, like dog, age, count, and tip. In this case we want to use two words, because we’ll have one variable for your sign and one for your mate’s sign. There are a few different ways to name a variable when you want it to be more than one word. One option is to run all the words together, like yoursign. This was common in the 1970s, but it’s not used much today because it’s hard to read. Another option is to use an underscore, like your_sign. The underscore looks a bit like a space, so the two words look like they would when written normally in English. A third option is to run the words together and capitalize each word, like YourSign, or maybe capitalize all words but the first, like yourSign. The capitalization makes it easier to see where each word starts. Languages like Lisp use hyphens, like your-sign, but we can’t do that here because a hyphen in our language is used for subtraction, and it would look like you’re trying to subtract the variable sign from the variable your. In no modern language can you do the obvious thing, which is to use a space, like your sign. That’s because space is already used to separate variables from other things. For example, if you wanted to print the value of this variable, you’d write:

print your_sign

If you could use spaces in variable names, it would be:

print your sign

But what if you had a variable that started with the word print, like maybe a variable called print count that stored how many times you wanted something printed? If you wanted to set this variable to 5, you’d write:

print count = 5

and the computer would be very confused, since at first it would look like you wanted to display the value of the variable “count”, and then later it would become clear that you really want to set the value of print count to 5. Since languages are designed to minimize the number of times that there’s guesswork or possible confusion, language designers don’t allow spaces. The underscore works fine, just imagine a space when you see it. The computer will see the underscore, which it treats just like any other letter, so it won’t be confused between:

print_count = 5

and:

print count

So back to our program. It looks like this:

print "What's your sign?"
your_sign = input()

We now have a variable, your_sign, which stores your zodiac sign as a string. We can use the value of this variable to display different things. For example, for Aries:

if your_sign == "Aries":
   print "Assertive, impulsive, defensive"

This compares the variable your_sign with the string “Aries”. If they’re the same, then we print a number of adjectives. If they’re not the same (if you’re not an Aries), then we do nothing and move on. We can write a bunch of these in a row to test for all the different signs:

if your_sign == "Aries":
    print "Assertive, impulsive, defensive"
if your_sign == "Taurus":
    print "Resourceful, thorough, indulgent"
if your_sign == "Gemini":
    print "Logical, inquisitive, fast"
if your_sign == "Cancer":
    print "Protective, sensitive, clinging"
if your_sign == "Leo":
    print "Generous, proud, theatrical"
if your_sign == "Virgo":
    print "Practical, efficient, critical"
if your_sign == "Libra":
    print "Co-operative, fair, lazy"
if your_sign == "Scorpio":
    print "Passionate, sensitive, anxious"
if your_sign == "Sagittarius":
    print "Free, straightforward, careless"
if your_sign == "Capricorn":
    print "Prudent, cautious, suspicious"
if your_sign == "Aquarius":
    print "Democratic, unconventional, detached"
if your_sign == "Pisces":
    print "Imaginative, sensitive, distracted"

Let’s repeat all that for your mate, using the variable mate_sign instead and changing the question on the first line:

print "What's your mate's sign?"
mate_sign = input()
if mate_sign == "Aries":
    print "Assertive, impulsive, defensive"
if mate_sign == "Taurus":
    print "Resourceful, thorough, indulgent"
if mate_sign == "Gemini":
    print "Logical, inquisitive, fast"
if mate_sign == "Cancer":
    print "Protective, sensitive, clinging"
if mate_sign == "Leo":
    print "Generous, proud, theatrical"
if mate_sign == "Virgo":
    print "Practical, efficient, critical"
if mate_sign == "Libra":
    print "Co-operative, fair, lazy"
if mate_sign == "Scorpio":
    print "Passionate, sensitive, anxious"
if mate_sign == "Sagittarius":
    print "Free, straightforward, careless"
if mate_sign == "Capricorn":
    print "Prudent, cautious, suspicious"
if mate_sign == "Aquarius":
    print "Democratic, unconventional, detached"
if mate_sign == "Pisces":
    print "Imaginative, sensitive, distracted"

There you go. You can now run this program and the computer will tell you something about you and your mate:

What's your sign?
Aquarius
Democratic, unconventional, detached
What's your mate's sign?
Scorpio
Passionate, sensitive, anxious

In this example, you can deduce that you and your mate are very compatible. When testing your programs, you always want to try not only valid input, but invalid input as well. Let’s try some invalid input to see how the program behaves:

What's your sign?
Yogurt
What's your mate's sign?
Volvo

Okay, that’s not so great. None of the if statements above matched, so nothing was printed for either your sign or your mate’s sign. That’s not very friendly. We’d like to tell the user that the text they entered wasn’t a valid sign. This lengthy if statement will do the trick:

if your_sign != "Aries" and your_sign != "Taurus" and \
    your_sign != "Gemini" and your_sign != "Cancer" and \
    your_sign != "Leo" and your_sign != "Virgo" and \
    your_sign != "Libra" and your_sign != "Scorpio" and \
    your_sign != "Sagittarius" and your_sign != "Capricorn" and \
    your_sign != "Aquarius" and your_sign != "Pisces":

    print "That's not a valid sign."
    stop()

There are a few new things to explain in that line. Normally we write if statements like this:

if age == 30:

We’ve also written more complex ones like this:

if age >= 40 and age < 50:

You could get more and more complex:

if (age >= 20 and age < 30) or (age >= 60 and age < 70):

If you had many more things to test all at once, the line would get very long. You could keep adding them to the same line, and the computer would accept that, but in your text editor it would be difficult to manage. You would either have to use the horizontal scroll bar a lot, or the text editor would automatically wrap the text for you, which may not look right to you. What you can do is split the line yourself. With that very last example you could write:

if (age >= 20 and age < 30) or
   (age >= 60 and age < 70):

That looks nice and neat because the two parenthesized pairs line up. But that would confuse the computer, because the computer expects one line of text to be one statement. So you explicitly tell it that the above two lines are supposed to be treated as a single line, as if you had written them all on one line. That’s done using a backslash at the end of the first line:

if (age >= 20 and age < 30) or \
   (age >= 60 and age < 70):

Imagine the backslash to mean, “Ignore this end-of-line here, pretend that the next line continued right here on this line.” That explains the five backslashes in our long if statement above: they made the entire six-line if statement look like a single line to the computer, which is what you need since if statements must be on a single line.

Next, let’s see what the logic is doing. It’s comparing the variable your_sign to the string “Aries” to see if they’re equal. If they’re not, and also your_sign is not equal to “Taurus”, and also not equal to “Gemini”, etc., then it prints the message. Remember that != means “not equal to”. Let’s “play computer” assuming that your_sign is the string “Yogurt”. The computer will see that first comparison, look up the value of your_sign, find that it’s “Yogurt”, and compare it to “Aries”:

if "Yogurt" != "Aries" and ...

Well that’s certainly true, the string “Yogurt” and “Aries” are different. It would then do the same thing with the next sign, find again that “Yogurt” didn’t equal “Taurus”, and so on. Since all twelve comparisons would succeed, the notice would be written to the user. The next line is:

stop()

That tells the computer to stop running the program. We don’t want to continue and ask for the mate’s sign. In a nicer program we wouldn’t stop, we would go back and ask for your sign again, but we won’t add that complication here.

If the sign is valid, say “Libra”, then that one comparison will fail:

... and your_sign != "Libra" and ...

This gets turned into:

... and "Libra" != "Libra" and ...

but that’s not true, “Libra” isn’t not equal to “Libra”. So the entire if statement would be false, the message would not be printed, and the program would not stop. Remember that if you have a bunch of things separated by and, they must all be true for the if to succeed. If you’re not familiar with the rules of logic, review the above if statement again carefully until it makes sense.

Back to our program. We would put this right after getting the sign from the user:

print "What's your sign?"
your_sign = input()
if your_sign != "Aries" and your_sign != "Taurus" and \
    your_sign != "Gemini" and your_sign != "Cancer" and \
    your_sign != "Leo" and your_sign != "Virgo" and \
    your_sign != "Libra" and your_sign != "Scorpio" and \
    your_sign != "Sagittarius" and your_sign != "Capricorn" and \
    your_sign != "Aquarius" and your_sign != "Pisces":

    print "That's not a valid sign."
    stop()
if your_sign == "Aries":
    print "Assertive, impulsive, defensive"
...

Great, now our program behaves a bit more nicely when you give it invalid input:

What's your sign?
Sweden
That's not a valid sign.

Of course remember to add this for the mate’s sign too later on down the program:

...
print "What's your mate's sign?"
mate_sign = input()
if mate_sign != "Aries" and mate_sign != "Taurus" and \
    mate_sign != "Gemini" and mate_sign != "Cancer" and \
    mate_sign != "Leo" and mate_sign != "Virgo" and \
    mate_sign != "Libra" and mate_sign != "Scorpio" and \
    mate_sign != "Sagittarius" and mate_sign != "Capricorn" and \
    mate_sign != "Aquarius" and mate_sign != "Pisces":

    print "That's not a valid sign."
    stop()
if mate_sign == "Aries":
    print "Assertive, impulsive, defensive"
...

Yet, something should bother us about this solution. One important goal of this book is to teach you how to write good code, not just functional code. And good code is beautiful. The above if statement is not beautiful. I don’t mean that it looks ugly, though it does. I mean that we’re comparing the variable your_sign against each sign twice. Once to see if it’s valid, and again later to print out an appropriate description. That should set off warning bells in our heads. There should be a nicer way to write this so that we only need to compare it once. After all, if nothing gets displayed, then the sign was invalid. We should be able to use that to determine the validity of the sign.

In an earlier chapter we had used an else statement to do something by default, in case the if statement didn’t do anything:

if age < 25:
    print "You're young."
else:
    print "You're mature!"

If there were only one star sign, we could use that same technique here to solve our problem:

if your_sign == "Aries":
    print "Assertive, impulsive, defensive"
else:
    print "That's not a valid sign."
    stop()

If we had two signs, we could write:

if your_sign == "Aries":
    print "Assertive, impulsive, defensive"
else:
    if your_sign == "Taurus":
        print "Resourceful, thorough, indulgent"
    else:
        print "That's not a valid sign."
        stop()

(Stop and work through the logic of that if it’s not clear what’s going on.) Three signs:

if your_sign == "Aries":
    print "Assertive, impulsive, defensive"
else:
    if your_sign == "Taurus":
        print "Resourceful, thorough, indulgent"
    else:
        if your_sign == "Gemini":
            print "Logical, inquisitive, fast"
        else:
            print "That's not a valid sign."
            stop()

This will work very nicely, but we keep indenting to the right with each sign. With twelve signs we’ll be all the way to the right of the screen. This is a common problem, so the language provides a statement called “else if”, written elif for short. The above three signs would be written like this:

if your_sign == "Aries":
    print "Assertive, impulsive, defensive"
elif your_sign == "Taurus":
    print "Resourceful, thorough, indulgent"
elif your_sign == "Gemini":
    print "Logical, inquisitive, fast"
else:
    print "That's not a valid sign."
    stop()

You can see how this would extend to twelve signs. In fact we could take our original twelve if statements, write “el” in front of all but the first one, and add our else clause at the end to catch errors. What would happen, then, is that the first if would be checked. If it succeeded, the first print statement would happen, and the program would continue after the entire section. Remember that elif stands for “else if”, so since the if succeeded, there’s no need to run the “else” part, and we skip the rest.

But if the first if is not successful, then we check the second one, then the third one, and so on until we find one that matches. If none match, then we display the error message. Let’s modify the whole thing, and remember again to update the mate’s section as well. Here’s the whole program so far:

print "What's your sign?"
your_sign = input()
if your_sign == "Aries":
    print "Assertive, impulsive, defensive"
elif your_sign == "Taurus":
    print "Resourceful, thorough, indulgent"
elif your_sign == "Gemini":
    print "Logical, inquisitive, fast"
elif your_sign == "Cancer":
    print "Protective, sensitive, clinging"
elif your_sign == "Leo":
    print "Generous, proud, theatrical"
elif your_sign == "Virgo":
    print "Practical, efficient, critical"
elif your_sign == "Libra":
    print "Co-operative, fair, lazy"
elif your_sign == "Scorpio":
    print "Passionate, sensitive, anxious"
elif your_sign == "Sagittarius":
    print "Free, straightforward, careless"
elif your_sign == "Capricorn":
    print "Prudent, cautious, suspicious"
elif your_sign == "Aquarius":
    print "Democratic, unconventional, detached"
elif your_sign == "Pisces":
    print "Imaginative, sensitive, distracted"
else:
    print "That's not a valid sign."
    stop()
print "What's your mate's sign?"
mate_sign = input()
if mate_sign == "Aries":
    print "Assertive, impulsive, defensive"
elif mate_sign == "Taurus":
    print "Resourceful, thorough, indulgent"
elif mate_sign == "Gemini":
    print "Logical, inquisitive, fast"
elif mate_sign == "Cancer":
    print "Protective, sensitive, clinging"
elif mate_sign == "Leo":
    print "Generous, proud, theatrical"
elif mate_sign == "Virgo":
    print "Practical, efficient, critical"
elif mate_sign == "Libra":
    print "Co-operative, fair, lazy"
elif mate_sign == "Scorpio":
    print "Passionate, sensitive, anxious"
elif mate_sign == "Sagittarius":
    print "Free, straightforward, careless"
elif mate_sign == "Capricorn":
    print "Prudent, cautious, suspicious"
elif mate_sign == "Aquarius":
    print "Democratic, unconventional, detached"
elif mate_sign == "Pisces":
    print "Imaginative, sensitive, distracted"
else:
    print "That's not a valid sign."
    stop()

Updating that second part is annoying. Each time we come up with an improvement to the code for your sign, we have to do it for the mate’s sign. The code looks virtually identical, except for the variable your_sign is replaced with mate_sign. Worse, you risk introducing bugs every time you modify the other code. And what if you were displaying the signs of 15 people? Would we have to modify 15 complicated pieces of code? Again, a warning bell should go off. It’s not beautiful to have that much code duplication. There must be a more elegant way to do it. Finally, we get to this chapter’s feature, functions.

A function is a piece of code that you write once but use in many places in your program. In our astrological program, we’d like to use the entire if sequence twice, but for convenience we’d like to write it only once. Let’s start with a simpler example. This example won’t illustrate the benefits of functions, only their syntax and form. Then we can go back to our more complex example above. Let’s write a program to display our dog’s name three times:

print "My dog's name is Bort."
print "My dog's name is Bort."
print "My dog's name is Bort."

That’s code duplication, so let’s put that code into a function. We do that like this:

def display_the_name_of_my_dog():
    print "My dog's name is Bort."

The def is short for “define (a function)”. We’re telling the computer that we’re about to describe what this function should do. Next comes the name of the function. This can be whatever you want, but should describe what the function does. (The name of a function is a bit like the name of a variable, except that the function is named after what it does, while a variable is named after what it stores.) Here we chose “display_the_name_of_my_dog”. The set of parentheses after the name are required; I’ll discuss them later. After that line comes what’s called the body of the function. That’s what the function does, the code that you want the computer to run when you use the function. The body is indented, just like the part of the if statement that runs when the if succeeds.

When we want to use a function, we write its name followed by parentheses:

display_the_name_of_my_dog()

Actually we’ll need that line three times since we wanted the name displayed three times:

display_the_name_of_my_dog()
display_the_name_of_my_dog()
display_the_name_of_my_dog()

The parentheses here match the ones in the function definition, and again I’ll explain them later. When we use a function like this, we say that we call the function. It’s a bit like calling someone and asking them to do something. We’ve seen a few function calls before. We had seen the input function:

age = input()

and the stop function. Both did something, though you didn’t have to define those, they came built-in to the language. Here we’re defining our own function. Each time you call a function, the computer looks up the function by name, finds the body you had given for it, and runs the statements in the body. The body can have many statements, and all will be run each time the function is called. It’s a bit like having a mini-program within a bigger program.

Functions are useful because they reduce code duplication. You can write something once instead of many times. But if that were their only advantage, they wouldn’t be all that useful. For example, we wouldn’t be able to use them for our astrological program since the code isn’t identical: the variable name is different. Functions only become truly useful when they have parameter.

A function parameter is a variable that you give to the function when it starts. Normally in a program you have to define each variable before you use it. A parameter is a variable that’s already set when the function begins. You provide it when you call the function. In our simplistic example, let’s say that our dog’s name wasn’t always “Bort”, and we wanted the function to display a variety of names. We would change the definition to look like this:

def display_the_name_of_my_dog(name):
    print "My dog's name is " + name + "."

That’s what those parentheses are for. That’s where the parameters go. When you call the function, you would provide a value for the name parameter:

display_the_name_of_my_dog("Bort")
display_the_name_of_my_dog("Milou")
display_the_name_of_my_dog("Rocky")

Let’s carefully look at what’s happening here. In the definition, you’re telling the computer that you want to define a function called display_the_name_of_my_dog that takes one parameter called name. In the body of the function you can use name as a variable, in this case using it along with some text. (Remember the + with strings is concatenation, or displaying strings one after one another.)

When we call the function the first time, we’re giving the string “Bort”. That calls the function, and sets the variable name to the string “Bort”, but only for that one call. When the function is done, we continue our program at the second line, calling the function again but with name set to “Milou”. This happens a third time with “Rocky”. When we run our program we see this:

My dog's name is Bort.
My dog's name is Milou.
My dog's name is Rocky.

A function can have any number of arguments, separated by commas:

def display_the_name_of_my_pet(pet, name):
    print "My " + pet + "'s name is " + name + "."

display_the_name_of_my_pet("dog", "Bort")
display_the_name_of_my_pet("cat", "Milou")
display_the_name_of_my_pet("mouse", "Rocky")

This would display:

My dog's name is Bort.
My cat's name is Milou.
My mouse's name is Rocky.

Note that when you call a function, you must provide as many parameters (separated by commas) as there are in the definition. Also note that you must define a function before you call it. That’s like setting a variable before you use it.

So back again to our astrological program. How do we use a function so that we can avoid having to update both parts of the program? Let’s put the if section into a function, like this:

def display_sign_information(sign):
    if sign == "Aries":
        print "Assertive, impulsive, defensive"
    elif sign == "Taurus":
        print "Resourceful, thorough, indulgent"
    elif sign == "Gemini":
        print "Logical, inquisitive, fast"
    elif sign == "Cancer":
        print "Protective, sensitive, clinging"
    elif sign == "Leo":
        print "Generous, proud, theatrical"
    elif sign == "Virgo":
        print "Practical, efficient, critical"
    elif sign == "Libra":
        print "Co-operative, fair, lazy"
    elif sign == "Scorpio":
        print "Passionate, sensitive, anxious"
    elif sign == "Sagittarius":
        print "Free, straightforward, careless"
    elif sign == "Capricorn":
        print "Prudent, cautious, suspicious"
    elif sign == "Aquarius":
        print "Democratic, unconventional, detached"
    elif sign == "Pisces":
        print "Imaginative, sensitive, distracted"
    else:
        print "That's not a valid sign."
        stop()

We used the generic name sign for the parameter instead of your_sign or mate_sign since we don’t know whose sign it is. This is just a generic function to display information for any sign.

Our main program just got a lot simpler:

print "What's your sign?"
your_sign = input()
display_sign_information(your_sign)

print "What's your mate's sign?"
mate_sign = input()
display_sign_information(mate_sign)

That entire if section can be written only once now, but used twice in our program. There are other benefits, too. For example, the if section now has a name (display_sign_information), which helps explain what it does. Someone reading the code can see the function’s name and quickly understand what it’s doing, rather than having to read the code. Another advantage is that our main program is a lot shorter and clearer. We can look at those six lines and quickly grasp what it’s doing. Before we had to read over 58 lines to get a high-level overview of the program.

Let’s make the output of this program a little nicer. Instead of just dumping out some adjectives, we can write a nicer sentence, like:

Scorpio people are passionate, sensitive, anxious.

We could change all of the print statements. The good news is that, having put our code into a function, we would need to change 12 lines instead of 24. The bad news is that a repetitive change like this should require only a single line of change, not 12. The twelve print statements are an indication that we’re not writing our code in a very elegant or flexible way. We can refactor the code to put the adjectives into a variable, like this:

def display_sign_information(sign):
    if sign == "Aries":
        adjectives = "assertive, impulsive, defensive"
    elif sign == "Taurus":
        adjectives = "resourceful, thorough, indulgent"
    elif sign == "Gemini":
        adjectives = "logical, inquisitive, fast"
    elif sign == "Cancer":
        adjectives = "protective, sensitive, clinging"
    elif sign == "Leo":
        adjectives = "generous, proud, theatrical"
    elif sign == "Virgo":
        adjectives = "practical, efficient, critical"
    elif sign == "Libra":
        adjectives = "co-operative, fair, lazy"
    elif sign == "Scorpio":
        adjectives = "passionate, sensitive, anxious"
    elif sign == "Sagittarius":
        adjectives = "free, straightforward, careless"
    elif sign == "Capricorn":
        adjectives = "prudent, cautious, suspicious"
    elif sign == "Aquarius":
        adjectives = "democratic, unconventional, detached"
    elif sign == "Pisces":
        adjectives = "imaginative, sensitive, distracted"
    else:
        print "That's not a valid sign."
        stop()
    print adjectives

Here I put the adjectives into a variable (called adjectives), made the first letter of each string lower case (so it could be used in a sentence), and moved the twelve print statements to a single one at the end. By refactoring my code this way, I can easily change what’s being displayed by modifying that one last line:

    print sign + " people are " + adjectives + "."

If I later want to change it again, I can do so easily. In fact, maybe the main program wants some control over what to display. An even nicer thing to write would be:

You are democratic, unconventional, detached.

and:

Your mate is passionate, sensitive, anxious.

There are two ways of doing this. One option is to add another parameter to our function that specifies how to phrase what’s displayed:

def display_sign_information(sign, prefix):
    if sign == "Aries":
        adjectives = "assertive, impulsive, defensive"
    elif sign == "Taurus":
        adjectives = "resourceful, thorough, indulgent"
    elif sign == "Gemini":
        adjectives = "logical, inquisitive, fast"
    elif sign == "Cancer":
        adjectives = "protective, sensitive, clinging"
    elif sign == "Leo":
        adjectives = "generous, proud, theatrical"
    elif sign == "Virgo":
        adjectives = "practical, efficient, critical"
    elif sign == "Libra":
        adjectives = "co-operative, fair, lazy"
    elif sign == "Scorpio":
        adjectives = "passionate, sensitive, anxious"
    elif sign == "Sagittarius":
        adjectives = "free, straightforward, careless"
    elif sign == "Capricorn":
        adjectives = "prudent, cautious, suspicious"
    elif sign == "Aquarius":
        adjectives = "democratic, unconventional, detached"
    elif sign == "Pisces":
        adjectives = "imaginative, sensitive, distracted"
    else:
        print "That's not a valid sign."
        stop()
    print prefix + " " + adjectives + "."

Our main program would then look like this:

print "What's your sign?"
your_sign = input()
display_sign_information(your_sign, "You are")

print "What's your mate's sign?"
mate_sign = input()
display_sign_information(mate_sign, "Your mate is")

But what if the main program wants to do something else with the adjectives? Maybe it wants to display both sets together, like this:

You are democratic, unconventional, detached; they are passionate, sensitive, anxious.

This would be awkward to do with the function as written above. In fact the function has pretty limited usefulness because it makes an assumption about how the main program wants the results displayed. It would be more generally useful if it simply told the main program what the adjectives were and let the program do whatever it wanted with them. We do that with a return value. Here’s how it works, again with a simplistic example first:

def get_square_of_number(x):
     return x * x

width = input()
area = get_square_of_number(width)
print area

We’ve defined a function called get_square_of_number, which takes a single parameter x. It calculates the square of x (by multiplying it by itself), and returns that value to the main program, using a return statement. This is the inverse process of a parameter. With a parameter the main program sends a value to the function; with a return statement the function sends a value back to the main program. In the main program, the returned value appears where the function was called. In the example above, the input function was called, and what the user typed shows up in the place of “input()”, meaning that it’s assigned to the width variable. Then the value of width is passed to the get_square_of_number function as a parameter (though inside the function it’s in the x variable). The returned value (the square of x), then shows up in the main program and gets assigned to area, which is then displayed.

In our astrological program we can have the function return the adjectives instead of printing them:

def get_sign_information(sign):
    if sign == "Aries":
        adjectives = "assertive, impulsive, defensive"
    elif sign == "Taurus":
        adjectives = "resourceful, thorough, indulgent"
    elif sign == "Gemini":
        adjectives = "logical, inquisitive, fast"
    elif sign == "Cancer":
        adjectives = "protective, sensitive, clinging"
    elif sign == "Leo":
        adjectives = "generous, proud, theatrical"
    elif sign == "Virgo":
        adjectives = "practical, efficient, critical"
    elif sign == "Libra":
        adjectives = "co-operative, fair, lazy"
    elif sign == "Scorpio":
        adjectives = "passionate, sensitive, anxious"
    elif sign == "Sagittarius":
        adjectives = "free, straightforward, careless"
    elif sign == "Capricorn":
        adjectives = "prudent, cautious, suspicious"
    elif sign == "Aquarius":
        adjectives = "democratic, unconventional, detached"
    elif sign == "Pisces":
        adjectives = "imaginative, sensitive, distracted"
    else:
        print "That's not a valid sign."
        stop()

    return adjectives

We’ve renamed the function to make it clear that the function is used to get the adjectives associated with a sign. Note that our use of the adjectives variable earlier made this change very easy. We only changed the first and last line of the function. The main program looks like this:

print "What's your sign?"
your_sign = input()
your_adjectives = get_sign_information(your_sign)
print "You are " + your_adjectives + "."

print "What's your mate's sign?"
mate_sign = input()
mate_adjectives = get_sign_information(mate_sign)
print "Your mate is " + mate_adjectives + "."

This is much more flexible. Instead of those two print statements we could have a single one like this:

print "You are " + your_adjectives + "; they are " + mate_adjectives + "."

But we won’t do that. Instead, we’ll notice again that these eight lines contain a lot of duplication. The first four lines are virtually identical to the last four. Let’s put them into a function:

def display_sign_information(whose, prefix):
    print "What's " + whose + " sign?"
    sign = input()
    adjectives = get_sign_information(sign)
    print prefix + " " + adjectives + "."

Notice that functions can call other functions. The main program is now:

display_sign_information("your", "You are")
display_sign_information("your mate's", "Your mate is")

Code duplication has been minimized! We could now ask about any number of people by adding only a single line to our main program. That’s a lot better than having to duplicate 29 lines (and having to modify them each time you come up with an improvement). The high-level structure of our program is also immediately obvious now: We’re going to ask about two people’s signs. The process for asking is also obvious: We ask a question, get the answer, get the adjectives based on that answer, and print those adjectives. Our code is beautiful!

Functions break programs up into small chunks. They’re useful because they reduce code duplication, but they also help us think about what the program is doing, how it’s structured, and what its important processes are. Here’s our final program:

def get_sign_information(sign):
    if sign == "Aries":
        adjectives = "assertive, impulsive, defensive"
    elif sign == "Taurus":
        adjectives = "resourceful, thorough, indulgent"
    elif sign == "Gemini":
        adjectives = "logical, inquisitive, fast"
    elif sign == "Cancer":
        adjectives = "protective, sensitive, clinging"
    elif sign == "Leo":
        adjectives = "generous, proud, theatrical"
    elif sign == "Virgo":
        adjectives = "practical, efficient, critical"
    elif sign == "Libra":
        adjectives = "co-operative, fair, lazy"
    elif sign == "Scorpio":
        adjectives = "passionate, sensitive, anxious"
    elif sign == "Sagittarius":
        adjectives = "free, straightforward, careless"
    elif sign == "Capricorn":
        adjectives = "prudent, cautious, suspicious"
    elif sign == "Aquarius":
        adjectives = "democratic, unconventional, detached"
    elif sign == "Pisces":
        adjectives = "imaginative, sensitive, distracted"
    else:
        print "That's not a valid sign."
        stop()

    return adjectives

def display_sign_information(whose, prefix):
    print "What's " + whose + " sign?"
    sign = input()
    adjectives = get_sign_information(sign)
    print prefix + " " + adjectives + "."

display_sign_information("your", "You are")
display_sign_information("your mate's", "Your mate is")

Compare that to the program we had before we broke it up into functions:

print "What's your sign?"
your_sign = input()
if your_sign == "Aries":
    print "Assertive, impulsive, defensive"
elif your_sign == "Taurus":
    print "Resourceful, thorough, indulgent"
elif your_sign == "Gemini":
    print "Logical, inquisitive, fast"
elif your_sign == "Cancer":
    print "Protective, sensitive, clinging"
elif your_sign == "Leo":
    print "Generous, proud, theatrical"
elif your_sign == "Virgo":
    print "Practical, efficient, critical"
elif your_sign == "Libra":
    print "Co-operative, fair, lazy"
elif your_sign == "Scorpio":
    print "Passionate, sensitive, anxious"
elif your_sign == "Sagittarius":
    print "Free, straightforward, careless"
elif your_sign == "Capricorn":
    print "Prudent, cautious, suspicious"
elif your_sign == "Aquarius":
    print "Democratic, unconventional, detached"
elif your_sign == "Pisces":
    print "Imaginative, sensitive, distracted"
else:
    print "That's not a valid sign."
    stop()
print "What's your mate's sign?"
mate_sign = input()
if mate_sign == "Aries":
    print "Assertive, impulsive, defensive"
elif mate_sign == "Taurus":
    print "Resourceful, thorough, indulgent"
elif mate_sign == "Gemini":
    print "Logical, inquisitive, fast"
elif mate_sign == "Cancer":
    print "Protective, sensitive, clinging"
elif mate_sign == "Leo":
    print "Generous, proud, theatrical"
elif mate_sign == "Virgo":
    print "Practical, efficient, critical"
elif mate_sign == "Libra":
    print "Co-operative, fair, lazy"
elif mate_sign == "Scorpio":
    print "Passionate, sensitive, anxious"
elif mate_sign == "Sagittarius":
    print "Free, straightforward, careless"
elif mate_sign == "Capricorn":
    print "Prudent, cautious, suspicious"
elif mate_sign == "Aquarius":
    print "Democratic, unconventional, detached"
elif mate_sign == "Pisces":
    print "Imaginative, sensitive, distracted"
else:
    print "That's not a valid sign."
    stop()