Automate the Boring Stuff with Python

Home > Other > Automate the Boring Stuff with Python > Page 7
Automate the Boring Stuff with Python Page 7

by Al Sweigart


  Since there is no parameter named eggs or any code that assigns eggs a value in the spam() function, when eggs is used in spam(), Python considers it a reference to the global variable eggs. This is why 42 is printed when the previous program is run.

  Local and Global Variables with the Same Name

  To simplify your life, avoid using local variables that have the same name as a global variable or another local variable. But technically, it’s perfectly legal to do so in Python. To see what happens, type the following code into the file editor and save it as sameName.py:

  def spam(): ➊ eggs = 'spam local' print(eggs) # prints 'spam local' def bacon(): ➋ eggs = 'bacon local' print(eggs) # prints 'bacon local' spam() print(eggs) # prints 'bacon local' ➌ eggs = 'global' bacon() print(eggs) # prints 'global'

  When you run this program, it outputs the following:

  bacon local spam local bacon local global

  There are actually three different variables in this program, but confusingly they are all named eggs. The variables are as follows:

  ➊ A variable named eggs that exists in a local scope when spam() is called.

  ➋ A variable named eggs that exists in a local scope when bacon() is called.

  ➌ A variable named eggs that exists in the global scope.

  Since these three separate variables all have the same name, it can be confusing to keep track of which one is being used at any given time. This is why you should avoid using the same variable name in different scopes.

  The global Statement

  If you need to modify a global variable from within a function, use the global statement. If you have a line such as global eggs at the top of a function, it tells Python, “In this function, eggs refers to the global variable, so don’t create a local variable with this name.” For example, type the following code into the file editor and save it as sameName2.py:

  def spam(): ➊ global eggs ➋ eggs = 'spam' eggs = 'global' spam() print(eggs)

  When you run this program, the final print() call will output this:

  spam

  Because eggs is declared global at the top of spam() ➊, when eggs is set to 'spam' ➋, this assignment is done to the globally scoped eggs. No local eggs variable is created.

  There are four rules to tell whether a variable is in a local scope or global scope:

  If a variable is being used in the global scope (that is, outside of all functions), then it is always a global variable.

  If there is a global statement for that variable in a function, it is a global variable.

  Otherwise, if the variable is used in an assignment statement in the function, it is a local variable.

  But if the variable is not used in an assignment statement, it is a global variable.

  To get a better feel for these rules, here’s an example program. Type the following code into the file editor and save it as sameName3.py:

  def spam(): ➊ global eggs eggs = 'spam' # this is the global def bacon(): ➋ eggs = 'bacon' # this is a local def ham(): ➌ print(eggs) # this is the global eggs = 42 # this is the global spam() print(eggs)

  In the spam() function, eggs is the global eggs variable, because there’s a global statement for eggs at the beginning of the function ➊. In bacon(), eggs is a local variable, because there’s an assignment statement for it in that function ➋. In ham() ➌, eggs is the global variable, because there is no assignment statement or global statement for it in that function. If you run sameName3.py, the output will look like this:

  spam

  In a function, a variable will either always be global or always be local. There’s no way that the code in a function can use a local variable named eggs and then later in that same function use the global eggs variable.

  Note

  If you ever want to modify the value stored in a global variable from in a function, you must use a global statement on that variable.

  If you try to use a local variable in a function before you assign a value to it, as in the following program, Python will give you an error. To see this, type the following into the file editor and save it as sameName4.py:

  def spam(): print(eggs) # ERROR! ➊ eggs = 'spam local' ➋ eggs = 'global' spam()

  If you run the previous program, it produces an error message.

  Traceback (most recent call last): File "C:/test3784.py", line 6, in spam() File "C:/test3784.py", line 2, in spam print(eggs) # ERROR! UnboundLocalError: local variable 'eggs' referenced before assignment

  This error happens because Python sees that there is an assignment statement for eggs in the spam() function ➊ and therefore considers eggs to be local. But because print(eggs) is executed before eggs is assigned anything, the local variable eggs doesn’t exist. Python will not fall back to using the global eggs variable ➋.

  Functions as “Black Boxes”

  Often, all you need to know about a function are its inputs (the parameters) and output value; you don’t always have to burden yourself with how the function’s code actually works. When you think about functions in this high-level way, it’s common to say that you’re treating the function as a “black box.”

  This idea is fundamental to modern programming. Later chapters in this book will show you several modules with functions that were written by other people. While you can take a peek at the source code if you’re curious, you don’t need to know how these functions work in order to use them. And because writing functions without global variables is encouraged, you usually don’t have to worry about the function’s code interacting with the rest of your program.

  Exception Handling

  Right now, getting an error, or exception, in your Python program means the entire program will crash. You don’t want this to happen in real-world programs. Instead, you want the program to detect errors, handle them, and then continue to run.

  For example, consider the following program, which has a “divide-by-zero” error. Open a new file editor window and enter the following code, saving it as zeroDivide.py:

  def spam(divideBy): return 42 / divideBy print(spam(2)) print(spam(12)) print(spam(0)) print(spam(1))

  We’ve defined a function called spam, given it a parameter, and then printed the value of that function with various parameters to see what happens. This is the output you get when you run the previous code:

  21.0 3.5 Traceback (most recent call last): File "C:/zeroDivide.py", line 6, in print(spam(0)) File "C:/zeroDivide.py", line 2, in spam return 42 / divideBy ZeroDivisionError: division by zero

  A ZeroDivisionError happens whenever you try to divide a number by zero. From the line number given in the error message, you know that the return statement in spam() is causing an error.

  Errors can be handled with try and except statements. The code that could potentially have an error is put in a try clause. The program execution moves to the start of a following except clause if an error happens.

  You can put the previous divide-by-zero code in a try clause and have an except clause contain code to handle what happens when this error occurs.

  def spam(divideBy): try: return 42 / divideBy except ZeroDivisionError: print('Error: Invalid argument.') print(spam(2)) print(spam(12)) print(spam(0)) print(spam(1))

  When code in a try clause causes an error, the program execution immediately moves to the code in the except clause. After running that code, the execution continues as normal. The output of the previous program is as follows:

  21.0 3.5 Error: Invalid argument. None 42.0

  Note that any errors that occur in function calls in a try block will also be caught. Consider the following program, which instead has the spam() calls in the try block:

  def spam(divideBy): return 42 / divideBy try: print(spam(2)) print(spam(12)) print(spam(0)) print(spam(1)) except ZeroDivisionError: print('Error: Invalid argument.')

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

  21.0 3.5 Error: Invalid argument.

  The reason print(spam(1))
is never executed is because once the execution jumps to the code in the except clause, it does not return to the try clause. Instead, it just continues moving down as normal.

  A Short Program: Guess the Number

  The toy examples I’ve show you so far are useful for introducing basic concepts, but now let’s see how everything you’ve learned comes together in a more complete program. In this section, I’ll show you a simple “guess the number” game. When you run this program, the output will look something like this:

  I am thinking of a number between 1 and 20. Take a guess. 10 Your guess is too low. Take a guess. 15 Your guess is too low. Take a guess. 17 Your guess is too high. Take a guess. 16 Good job! You guessed my number in 4 guesses!

  Type the following source code into the file editor, and save the file as guessTheNumber.py:

  # This is a guess the number game. import random secretNumber = random.randint(1, 20) print('I am thinking of a number between 1 and 20.') # Ask the player to guess 6 times. for guessesTaken in range(1, 7): print('Take a guess.') guess = int(input()) if guess < secretNumber: print('Your guess is too low.') elif guess > secretNumber: print('Your guess is too high.') else: break # This condition is the correct guess! if guess == secretNumber: print('Good job! You guessed my number in ' + str(guessesTaken) + ' guesses!') else: print('Nope. The number I was thinking of was ' + str(secretNumber))

  Let’s look at this code line by line, starting at the top.

  # This is a guess the number game. import random secretNumber = random.randint(1, 20)

  First, a comment at the top of the code explains what the program does. Then, the program imports the random module so that it can use the random.randint() function to generate a number for the user to guess. The return value, a random integer between 1 and 20, is stored in the variable secretNumber.

  print('I am thinking of a number between 1 and 20.') # Ask the player to guess 6 times. for guessesTaken in range(1, 7): print('Take a guess.') guess = int(input())

  The program tells the player that it has come up with a secret number and will give the player six chances to guess it. The code that lets the player enter a guess and checks that guess is in a for loop that will loop at most six times. The first thing that happens in the loop is that the player types in a guess. Since input() returns a string, its return value is passed straight into int(), which translates the string into an integer value. This gets stored in a variable named guess.

  if guess < secretNumber: print('Your guess is too low.') elif guess > secretNumber: print('Your guess is too high.')

  These few lines of code check to see whether the guess is less than or greater than the secret number. In either case, a hint is printed to the screen.

  else: break # This condition is the correct guess!

  If the guess is neither higher nor lower than the secret number, then it must be equal to the secret number, in which case you want the program execution to break out of the for loop.

  if guess == secretNumber: print('Good job! You guessed my number in ' + str(guessesTaken) + ' guesses!') else: print('Nope. The number I was thinking of was ' + str(secretNumber))

  After the for loop, the previous if...else statement checks whether the player has correctly guessed the number and prints an appropriate message to the screen. In both cases, the program displays a variable that contains an integer value (guessesTaken and secretNumber). Since it must concatenate these integer values to strings, it passes these variables to the str() function, which returns the string value form of these integers. Now these strings can be concatenated with the + operators before finally being passed to the print() function call.

  Summary

  Functions are the primary way to compartmentalize your code into logical groups. Since the variables in functions exist in their own local scopes, the code in one function cannot directly affect the values of variables in other functions. This limits what code could be changing the values of your variables, which can be helpful when it comes to debugging your code.

  Functions are a great tool to help you organize your code. You can think of them as black boxes: They have inputs in the form of parameters and outputs in the form of return values, and the code in them doesn’t affect variables in other functions.

  In previous chapters, a single error could cause your programs to crash. In this chapter, you learned about try and except statements, which can run code when an error has been detected. This can make your programs more resilient to common error cases.

  Practice Questions

  Q:

  1. Why are functions advantageous to have in your programs?

  Q:

  2. When does the code in a function execute: when the function is defined or when the function is called?

  Q:

  3. What statement creates a function?

  Q:

  4. What is the difference between a function and a function call?

  Q:

  5. How many global scopes are there in a Python program? How many local scopes?

  Q:

  6. What happens to variables in a local scope when the function call returns?

  Q:

  7. What is a return value? Can a return value be part of an expression?

  Q:

  8. If a function does not have a return statement, what is the return value of a call to that function?

  Q:

  9. How can you force a variable in a function to refer to the global variable?

  Q:

  10. What is the data type of None?

  Q:

  11. What does the import areallyourpetsnamederic statement do?

  Q:

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

  Q:

  13. How can you prevent a program from crashing when it gets an error?

  Q:

  14. What goes in the try clause? What goes in the except clause?

  Practice Projects

  For practice, write programs to do the following tasks.

  The Collatz Sequence

  Write a function named collatz() that has one parameter named number. If number is even, then collatz() should print number // 2 and return this value. If number is odd, then collatz() should print and return 3 * number + 1.

  Then write a program that lets the user type in an integer and that keeps calling collatz() on that number until the function returns the value 1. (Amazingly enough, this sequence actually works for any integer—sooner or later, using this sequence, you’ll arrive at 1! Even mathematicians aren’t sure why. Your program is exploring what’s called the Collatz sequence, sometimes called “the simplest impossible math problem.”)

  Remember to convert the return value from input() to an integer with the int() function; otherwise, it will be a string value.

  Hint: An integer number is even if number % 2 == 0, and it’s odd if number % 2 == 1.

  The output of this program could look something like this:

  Enter number: 3 10 5 16 8 4 2 1

  Input Validation

  Add try and except statements to the previous project to detect whether the user types in a noninteger string. Normally, the int() function will raise a ValueError error if it is passed a noninteger string, as in int('puppy'). In the except clause, print a message to the user saying they must enter an integer.

  Chapter 4. Lists

  One more topic you’ll need to understand before you can begin writing programs in earnest is the list data type and its cousin, the tuple. Lists and tuples can contain multiple values, which makes it easier to write programs that handle large amounts of data. And since lists themselves can contain other lists, you can use them to arrange data into hierarchical structures.

  In this chapter, I’ll discuss the basics of lists. I’ll also teach you about methods, which are functions that are tied to values of a certain data type. Then I’ll briefly cover the list-like tuple and string data types and how they compare to list values. I
n the next chapter, I’ll introduce you to the dictionary data type.

  The List Data Type

  A list is a value that contains multiple values in an ordered sequence. The term list value refers to the list itself (which is a value that can be stored in a variable or passed to a function like any other value), not the values inside the list value. A list value looks like this: ['cat', 'bat', 'rat', 'elephant']. Just as string values are typed with quote characters to mark where the string begins and ends, a list begins with an opening square bracket and ends with a closing square bracket, []. Values inside the list are also called items. Items are separated with commas (that is, they are comma-delimited). For example, enter the following into the interactive shell:

  >>> [1, 2, 3] [1, 2, 3] >>> ['cat', 'bat', 'rat', 'elephant'] ['cat', 'bat', 'rat', 'elephant'] >>> ['hello', 3.1415, True, None, 42] ['hello', 3.1415, True, None, 42] ➊ >>> spam = ['cat', 'bat', 'rat', 'elephant'] >>> spam ['cat', 'bat', 'rat', 'elephant']

  The spam variable ➊ is still assigned only one value: the list value. But the list value itself contains other values. The value [] is an empty list that contains no values, similar to '', the empty string.

  Getting Individual Values in a List with Indexes

 

‹ Prev