Refinement: Case Study 2 (Sentinel-Controlled Repetition) Let us generalize the class-average problem Consider the following problem:
Line 15 combines two function calls—raw_input and int—into one statement In this case, function raw_input reads a value from the user, then the result is passed to
36 print "Player loses"
Player rolled 2 + 5 = 7 Player wins Player rolled 1 + 2 = 3 Player loses Player rolled 1 + 5 = 6 Point is 6 Player rolled 1 + 6 = 7 Player loses Player rolled 5 + 4 = 9 Point is 9 Player rolled 4 + 4 = 8 Player rolled 2 + 3 = 5 Player rolled 5 + 4 = 9 Player wins Fig. 4.8 Fig. 4.8 Fig. 4.8
The game is reasonably involved. The player could win or lose on the first roll or on any subsequent roll. The variable gameStatus keeps track of the win/loss status. Vari- able gameStatus is one of the strings "WON", "LOST" or "CONTINUE". When the player wins the game, gameStatus is set to "WON" (lines 17 and 29). When the player loses the game, gameStatus is set to "LOST" (lines 19 and 31). Otherwise,
gameStatus is set to "CONTINUE", allowing the dice to be rolled again (line 21). If the game is won or lost after the first roll, the body of the while structure (lines 25– 31) is skipped, because gameStatus is not equal to "CONTINUE" (line 25). Instead, the program proceeds to the if/else structure (lines 33–36), which prints "Player wins" if gameStatus equals "WON", but "Player loses" if gameStatus equals
"LOST".
If the game is not won or lost after the first roll, the value of sum is assigned to variable
myPoint (line 22). Execution proceeds with the while structure, because gameStatus equals "CONTINUE". During each iteration of the while loop, rollDice is invoked to produce a new sum (line 26). If sum matches myPoint, gameStatus is set to "WON" (lines 28–29), the while test fails (line 25), the if/else structure prints "Player
wins" (lines 33–34) and execution terminates. If sum is equal to 7, gameStatus is set to "LOST" (lines 30–31), the while test fails (line 25), the if/else statement prints
"Player loses" (lines 35–36) and execution terminates. Otherwise, the while loop continues executing.
Note the use of the various program-control mechanisms discussed earlier. The craps program uses one programmer-defined function—rollDice—and the while, if/else and if/elif/else structures. The program uses both stacked control structures (the if/
elif/else in lines 16–23 and the while in lines 25–31) and nested control structures (the if/elif in lines 28–31 is nested inside the while in lines 25–31).
4.8 Scope Rules
2Until now, we have not discussed how a Python program stores and retrieves a variable’s value. It appears that the value is simply “there” when the program needs it. In fact, Python has strict rules that describe how and when a variable’s value can be accessed. These rules are described in terms of namespaces and scopes. In this section, we discuss how namespaces and scopes affect a program’s execution.
We use an example to explain these concepts. Assume that a function contains the fol- lowing line of code:
print x
Before a value can be printed to the screen, Python must first find the identifier named x and determine the value associated with that identifier. Namespaces store information about an identifier and the value to which it is bound. Python defines three namespaces— local, global and built-in. When a program attempts to access an identifier’s value, Python searches the namespaces in a certain order—local, global and built-in namespaces—to see whether and where the identifier exists.
2. Nested scopes are not discussed in this text. Nested scopes are a complex topic and were optional in Python 2.1 but are mandatory in Python 2.2. Information about nested scopes can be found in PEP 227 at www.python.org/peps/pep-0227.html.
The first namespace that Python searches is the local namespace, which stores bind- ings created in a block. Function bodies are blocks, so all function parameters and any iden- tifiers the function creates are stored in the function’s local namespace. Each function has a unique local namespace—one function cannot access the local namespace of another function. In the example above, Python first searches the function’s local namespace for an identifier named x. If the function’s local namespace contains such an identifier, the func- tion prints the value of x to the screen. If the function’s local namespace does not contain an identifier named x (e.g., the function does not define any parameters or create any iden- tifiers named x), Python searches the next outer namespace—the global namespace (some- times called the module namespace).
The global namespace contains the bindings for all identifiers, function names and class names defined within a module or file. Each module or file’s global namespace con- tains an identifier called __name__ that states the module’s name (e.g., "math" or
"random"). When a Python interpreter session starts or when the Python interpreter begins executing a program stored in a file, the value of __name__ is "__main__". In the example above, Python searches for an identifier named x in the global namespace. If the global namespace contains the identifier (i.e., the identifier was bound to the global namespace before the function was called), Python stops searching for the identifier and the function prints the value of x to the screen. If the global namespace does not contain an identifier named x, Python searches the next outer namespace—the built-in namespace.
The built-in namespace contains identifiers that correspond to many Python functions and error messages. For example, functions raw_input, int and range belong to the built-in namespace. Python creates the built-in namespace when the interpreter starts, and programs normally do not modify the namespace (e.g., by adding an identifier to the namespace). In the example above, the built-in namespace does not contain an identifier named x, so Python stops searching and prints an error message stating that the identifier could not be found.
An identifier’s scope describes the region of a program that can access the identifier’s value. If an identifier is defined in the local namespace (e.g., in a function), all statements in the block may access that identifier. Statements that reside outside the block (e.g., in the main portion of a program or in another function) cannot access the identifier. Once the code block terminates (e.g., after a return statement), all identifiers in that block’s local namespace “go out of scope” and are inaccessible.
If an identifier is defined in the global namespace, the identifier has global scope. A global identifier is known to all code that executes, from the point at which the identifier is created until the end of the file. Furthermore, if certain criteria are met, functions may access global identifiers. We discuss this issue momentarily. Identifiers contained in built- in namespaces may be accessed by code in programs, modules or functions.
One pitfall that can arise in a program that uses functions is called shadowing. When a function creates a local identifier with the same name as an identifier in the module or built-in namespaces, the local identifier shadows the global or built-in identifier. A logic error can occur if the programmer references the local variable when meaning to reference the global or built-in identifier.
Common Programming Error 4.6
Shadowing an identifier in the module or built-in namespace with an identifier in the local
Good Programming Practice 4.5
Avoid variable names that shadow names in outer scopes. This can be accomplished by avoiding the use of an identifier with the same name as an identifier in the built-in namespace and by avoiding the use of duplicate identifiers in a program. 4.5 Python provides a way for programmers to determine what identifiers are available from the current namespace. Built-in function dir returns a list of these identifiers. Figure 4.9 shows the namespace that Python creates when starting an interactive session. Calling function dir tells us that the current namespace contains three identifiers:
__builtins__, __doc__ and __name__. The next command in the session prints the value for identifier __name__, to demonstrate that this value is __main__ for an inter- active session. The subsequent command prints the value for identifier __builtins__. Notice that we get back a value indicating that this identifier is bound to a module. This indicates that the identifier __builtins__ can be used to refer to the module
__builtin__.We explore this further in Section 4.9. The next command in the interac- tive session creates a new identifier x and binds it to the value 3. Calling function dir again reveals that identifier x has been added to the session’s namespace.
The interactive session in Fig. 4.9 only hints at a Python program’s powerful ability to provide information about the identifiers in a program (or interactive session). This is called introspection. Python provides many other introspective capabilities, including func- tions globals and locals that return additional information about the global and local namespaces, respectively.
Although functions help make a program easier to debug, scoping issues can introduce subtle errors into a program if the developer is not careful. The program in Fig. 4.10 dem- onstrates these issues, using global and local variables. Line 4 creates variable x with the value 1. This variable resides in the global namespace for the program and has global scope. In other words, variable x can be accessed and changed by any code that appears after line 4. This global variable is shadowed in any function that creates a local variable named x. In the main program, line 22 prints the value of variable x (i.e., 1). Lines 24–25 assign the value 7 to variable x and print its new value.
Python 2.2b2 (#26, Nov 16 2001, 11:44:11) [MSC 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more informa- tion.
>>> dir()
['__builtins__', '__doc__', '__name__'] >>> print __name__
__main__
>>> print __builtins__
<module '__builtin__' (built-in)>
>>> x = 3 # bind new identifier to global namespace >>> dir()
['__builtins__', '__doc__', '__name__', 'x']
Fig. 4.9 Fig. 4.9 Fig. 4.9
The program defines two functions that neither receive nor return any arguments. Function a (lines 7–12) declares a local variable x and initializes it to 25. Then, function a prints local variable x, increments it and prints it again (lines 10–12). Each time the pro- 1 # Fig. 4.10: fig04_10.py
2 # Scoping example. 3
4 x = 1 # global variable 5
6 # alters the local variable x, shadows the global variable 7 def a():
8 x = 25
9
10 print "\nlocal x in a is", x, "after entering a"
11 x += 1
12 print "local x in a is", x, "before exiting a"
13
14 # alters the global variable x 15 def b():
16 global x 17
18 print "\nglobal x is", x, "on entering b"
19 x *= 10
20 print "global x is", x, "on exiting b"
21
22 print "global x is", x 23
24 x = 7
25 print "global x is", x 26 27 a() 28 b() 29 a() 30 b() 31
32 print "\nglobal x is", x
global x is 1 global x is 7
local x in a is 25 after entering a