Book Read Free

Automate the Boring Stuff with Python

Page 6

by Al Sweigart


  Summary

  By using expressions that evaluate to True or False (also called conditions), you can write programs that make decisions on what code to execute and what code to skip. You can also execute code over and over again in a loop while a certain condition evaluates to True. The break and continue statements are useful if you need to exit a loop or jump back to the start.

  These flow control statements will let you write much more intelligent programs. There’s another type of flow control that you can achieve by writing your own functions, which is the topic of the next chapter.

  Practice Questions

  Q:

  1. What are the two values of the Boolean data type? How do you write them?

  Q:

  2. What are the three Boolean operators?

  Q:

  3. Write out the truth tables of each Boolean operator (that is, every possible combination of Boolean values for the operator and what they evaluate to).

  Q:

  4. What do the following expressions evaluate to?

  (5 > 4) and (3 == 5) not (5 > 4) (5 > 4) or (3 == 5) not ((5 > 4) or (3 == 5)) (True and True) and (True == False) (not False) or (not True)

  Q:

  5. What are the six comparison operators?

  Q:

  6. What is the difference between the equal to operator and the assignment operator?

  Q:

  7. Explain what a condition is and where you would use one.

  Q:

  8. Identify the three blocks in this code:

  spam = 0 if spam == 10: print('eggs') if spam > 5: print('bacon') else: print('ham') print('spam') print('spam')

  Q:

  9. Write code that prints Hello if 1 is stored in spam, prints Howdy if 2 is stored in spam, and prints Greetings! if anything else is stored in spam.

  Q:

  10. What can you press if your program is stuck in an infinite loop?

  Q:

  11. What is the difference between break and continue?

  Q:

  12. What is the difference between range(10), range(0, 10), and range(0, 10, 1) in a for loop?

  Q:

  13. Write a short program that prints the numbers 1 to 10 using a for loop. Then write an equivalent program that prints the numbers 1 to 10 using a while loop.

  Q:

  14. If you had a function named bacon() inside a module named spam, how would you call it after importing spam?

  Extra credit: Look up the round() and abs() functions on the Internet, and find out what they do. Experiment with them in the interactive shell.

  Chapter 3. Functions

  You’re already familiar with the print(), input(), and len() functions from the previous chapters. Python provides several builtin functions like these, but you can also write your own functions. A function is like a mini-program within a program.

  To better understand how functions work, let’s create one. Type this program into the file editor and save it as helloFunc.py:

  ➊ def hello(): ➋ print('Howdy!') print('Howdy!!!') print('Hello there.') ➌ hello() hello() hello()

  The first line is a def statement ➊, which defines a function named hello(). The code in the block that follows the def statement ➋ is the body of the function. This code is executed when the function is called, not when the function is first defined.

  The hello() lines after the function ➌ are function calls. In code, a function call is just the function’s name followed by parentheses, possibly with some number of arguments in between the parentheses. When the program execution reaches these calls, it will jump to the top line in the function and begin executing the code there. When it reaches the end of the function, the execution returns to the line that called the function and continues moving through the code as before.

  Since this program calls hello() three times, the code in the hello() function is executed three times. When you run this program, the output looks like this:

  Howdy! Howdy!!! Hello there. Howdy! Howdy!!! Hello there. Howdy! Howdy!!! Hello there.

  A major purpose of functions is to group code that gets executed multiple times. Without a function defined, you would have to copy and paste this code each time, and the program would look like this:

  print('Howdy!') print('Howdy!!!') print('Hello there.') print('Howdy!') print('Howdy!!!') print('Hello there.') print('Howdy!') print('Howdy!!!') print('Hello there.')

  In general, you always want to avoid duplicating code, because if you ever decide to update the code—if, for example, you find a bug you need to fix—you’ll have to remember to change the code everywhere you copied it.

  As you get more programming experience, you’ll often find yourself deduplicating code, which means getting rid of duplicated or copy-and-pasted code. Deduplication makes your programs shorter, easier to read, and easier to update.

  def Statements with Parameters

  When you call the print() or len() function, you pass in values, called arguments in this context, by typing them between the parentheses. You can also define your own functions that accept arguments. Type this example into the file editor and save it as helloFunc2.py:

  ➊ def hello(name): ➋ print('Hello ' + name) ➌ hello('Alice') hello('Bob')

  When you run this program, the output looks like this:

  Hello Alice Hello Bob

  The definition of the hello() function in this program has a parameter called name ➊. A parameter is a variable that an argument is stored in when a function is called. The first time the hello() function is called, it’s with the argument 'Alice' ➌. The program execution enters the function, and the variable name is automatically set to 'Alice', which is what gets printed by the print() statement ➋.

  One special thing to note about parameters is that the value stored in a parameter is forgotten when the function returns. For example, if you added print(name) after hello('Bob') in the previous program, the program would give you a NameError because there is no variable named name. This variable was destroyed after the function call hello('Bob') had returned, so print(name) would refer to a name variable that does not exist.

  This is similar to how a program’s variables are forgotten when the program terminates. I’ll talk more about why that happens later in the chapter, when I discuss what a function’s local scope is.

  Return Values and return Statements

  When you call the len() function and pass it an argument such as 'Hello', the function call evaluates to the integer value 5, which is the length of the string you passed it. In general, the value that a function call evaluates to is called the return value of the function.

  When creating a function using the def statement, you can specify what the return value should be with a return statement. A return statement consists of the following:

  The return keyword

  The value or expression that the function should return

  When an expression is used with a return statement, the return value is what this expression evaluates to. For example, the following program defines a function that returns a different string depending on what number it is passed as an argument. Type this code into the file editor and save it as magic8Ball.py:

  ➊ import random ➋ def getAnswer(answerNumber): ➌ if answerNumber == 1: return 'It is certain' elif answerNumber == 2: return 'It is decidedly so' elif answerNumber == 3: return 'Yes' elif answerNumber == 4: return 'Reply hazy try again' elif answerNumber == 5: return 'Ask again later' elif answerNumber == 6: return 'Concentrate and ask again' elif answerNumber == 7: return 'My reply is no' elif answerNumber == 8: return 'Outlook not so good' elif answerNumber == 9: return 'Very doubtful' ➍ r = random.randint(1, 9) ➎ fortune = getAnswer(r) ➏ print(fortune)

  When this program starts, Python first imports the random module ➊. Then the getAnswer() function is defined ➋. Because the function is being defined (and not called), the execution skips over the code in it. Next, the random.randint() function is called with two arguments, 1 and 9 ➍. It
evaluates to a random integer between 1 and 9 (including 1 and 9 themselves), and this value is stored in a variable named r.

  The getAnswer() function is called with r as the argument ➎. The program execution moves to the top of the getAnswer() function ➌, and the value r is stored in a parameter named answerNumber. Then, depending on this value in answerNumber, the function returns one of many possible string values. The program execution returns to the line at the bottom of the program that originally called getAnswer() ➎. The returned string is assigned to a variable named fortune, which then gets passed to a print() call ➏ and is printed to the screen.

  Note that since you can pass return values as an argument to another function call, you could shorten these three lines:

  r = random.randint(1, 9) fortune = getAnswer(r) print(fortune)

  to this single equivalent line:

  print(getAnswer(random.randint(1, 9)))

  Remember, expressions are composed of values and operators. A function call can be used in an expression because it evaluates to its return value.

  The None Value

  In Python there is a value called None, which represents the absence of a value. None is the only value of the NoneType data type. (Other programming languages might call this value null, nil, or undefined.) Just like the Boolean True and False values, None must be typed with a capital N.

  This value-without-a-value can be helpful when you need to store something that won’t be confused for a real value in a variable. One place where None is used is as the return value of print(). The print() function displays text on the screen, but it doesn’t need to return anything in the same way len() or input() does. But since all function calls need to evaluate to a return value, print() returns None. To see this in action, enter the following into the interactive shell:

  >>> spam = print('Hello!') Hello! >>> None == spam True

  Behind the scenes, Python adds return None to the end of any function definition with no return statement. This is similar to how a while or for loop implicitly ends with a continue statement. Also, if you use a return statement without a value (that is, just the return keyword by itself), then None is returned.

  Keyword Arguments and print()

  Most arguments are identified by their position in the function call. For example, random.randint(1, 10) is different from random.randint(10, 1). The function call random.randint(1, 10) will return a random integer between 1 and 10, because the first argument is the low end of the range and the second argument is the high end (while random.randint(10, 1) causes an error).

  However, keyword arguments are identified by the keyword put before them in the function call. Keyword arguments are often used for optional parameters. For example, the print() function has the optional parameters end and sep to specify what should be printed at the end of its arguments and between its arguments (separating them), respectively.

  If you ran the following program:

  print('Hello') print('World')

  the output would look like this:

  Hello World

  The two strings appear on separate lines because the print() function automatically adds a newline character to the end of the string it is passed. However, you can set the end keyword argument to change this to a different string. For example, if the program were this:

  print('Hello', end='') print('World')

  the output would look like this:

  HelloWorld

  The output is printed on a single line because there is no longer a new-line printed after 'Hello'. Instead, the blank string is printed. This is useful if you need to disable the newline that gets added to the end of every print() function call.

  Similarly, when you pass multiple string values to print(), the function will automatically separate them with a single space. Enter the following into the interactive shell:

  >>> print('cats', 'dogs', 'mice') cats dogs mice

  But you could replace the default separating string by passing the sep keyword argument. Enter the following into the interactive shell:

  >>> print('cats', 'dogs', 'mice', sep=',') cats,dogs,mice

  You can add keyword arguments to the functions you write as well, but first you’ll have to learn about the list and dictionary data types in the next two chapters. For now, just know that some functions have optional keyword arguments that can be specified when the function is called.

  Local and Global Scope

  Parameters and variables that are assigned in a called function are said to exist in that function’s local scope. Variables that are assigned outside all functions are said to exist in the global scope. A variable that exists in a local scope is called a local variable, while a variable that exists in the global scope is called a global variable. A variable must be one or the other; it cannot be both local and global.

  Think of a scope as a container for variables. When a scope is destroyed, all the values stored in the scope’s variables are forgotten. There is only one global scope, and it is created when your program begins. When your program terminates, the global scope is destroyed, and all its variables are forgotten. Otherwise, the next time you ran your program, the variables would remember their values from the last time you ran it.

  A local scope is created whenever a function is called. Any variables assigned in this function exist within the local scope. When the function returns, the local scope is destroyed, and these variables are forgotten. The next time you call this function, the local variables will not remember the values stored in them from the last time the function was called.

  Scopes matter for several reasons:

  Code in the global scope cannot use any local variables.

  However, a local scope can access global variables.

  Code in a function’s local scope cannot use variables in any other local scope.

  You can use the same name for different variables if they are in different scopes. That is, there can be a local variable named spam and a global variable also named spam.

  The reason Python has different scopes instead of just making everything a global variable is so that when variables are modified by the code in a particular call to a function, the function interacts with the rest of the program only through its parameters and the return value. This narrows down the list code lines that may be causing a bug. If your program contained nothing but global variables and had a bug because of a variable being set to a bad value, then it would be hard to track down where this bad value was set. It could have been set from anywhere in the program—and your program could be hundreds or thousands of lines long! But if the bug is because of a local variable with a bad value, you know that only the code in that one function could have set it incorrectly.

  While using global variables in small programs is fine, it is a bad habit to rely on global variables as your programs get larger and larger.

  Local Variables Cannot Be Used in the Global Scope

  Consider this program, which will cause an error when you run it:

  def spam(): eggs = 31337 spam() print(eggs)

  If you run this program, the output will look like this:

  Traceback (most recent call last): File "C:/test3784.py", line 4, in print(eggs) NameError: name 'eggs' is not defined

  The error happens because the eggs variable exists only in the local scope created when spam() is called. Once the program execution returns from spam, that local scope is destroyed, and there is no longer a variable named eggs. So when your program tries to run print(eggs), Python gives you an error saying that eggs is not defined. This makes sense if you think about it; when the program execution is in the global scope, no local scopes exist, so there can’t be any local variables. This is why only global variables can be used in the global scope.

  Local Scopes Cannot Use Variables in Other Local Scopes

  A new local scope is created whenever a function is called, including when a function is called from another function. Consider this program:

  def spam():
➊ eggs = 99 ➋ bacon() ➌ print(eggs) def bacon(): ham = 101 ➍ eggs = 0 ➎ spam()

  When the program starts, the spam() function is called ➎, and a local scope is created. The local variable eggs ➊ is set to 99. Then the bacon() function is called ➋, and a second local scope is created. Multiple local scopes can exist at the same time. In this new local scope, the local variable ham is set to 101, and a local variable eggs—which is different from the one in spam()’s local scope—is also created ➍ and set to 0.

  When bacon() returns, the local scope for that call is destroyed. The program execution continues in the spam() function to print the value of eggs ➌, and since the local scope for the call to spam() still exists here, the eggs variable is set to 99. This is what the program prints.

  The upshot is that local variables in one function are completely separate from the local variables in another function.

  Global Variables Can Be Read from a Local Scope

  Consider the following program:

  def spam(): print(eggs) eggs = 42 spam() print(eggs)

 

‹ Prev