• No results found

Binding names for modules and module identifiers

In document Python - How to Program (Page 140-143)

3. Actually, function dir returns a list of attributes for the object passed as an argument In the case of a module, this information amounts to a list of all identifiers (e.g., functions and data) defined in the module.

4.9.3 Binding names for modules and module identifiers

We have already seen how a program can import a module or specific identifiers from a mod- ule. Python’s syntax gives the programmer considerable control over how the import state- ment affects a program’s namespace. In this section, we discuss this control in more detail and explain further how the programmer can customize the references to imported elements.

The statement import random

imports the random module and places a reference to the module named random in the namespace. In the interactive session in Fig. 4.15, the statement

import random as randomModule

also imports the random module, but the as clause of the statement allows the program- mer to specify the name of the reference to the module. In this case, we create a reference named randomModule. Now, if we want to access the random module, we use refer- ence randomModule.

A program can also use an import/as statement to specify a name for an identifier that the program imports from a module. The line

from math import sqrt as squareRoot

imports the sqrt function from module math and creates a reference to the function named squareRoot. The programmer may now invoke the function with this reference. Typically, module authors use import/as statements, because the imported element may define names that conflict with identifiers already defined by the author’s module. With the import/as statement, the module author can specify a new name for the imported ele- ments and thereby avoid the naming conflict. Programmers also use the import/as state- ment for convenience. A programmer may use the statement to rename a particularly long identifier that the program uses extensively. The programmer specifies a shorter name for the identifier, thus increasing readability and decreasing the amount of typing.

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.

>>> import random as randomModule >>> dir()

['__builtins__', '__doc__', '__name__', 'randomModule'] >>> randomModule.randrange( 1, 7 )

1

>>> from math import sqrt as squareRoot >>> dir()

['__builtins__', '__doc__', '__name__', 'randomModule', 'square- Root'] >>> squareRoot( 9.0 ) 3.0 Fig. 4.15 Fig. 4.15 Fig. 4.15

Python’s capabilities for importing elements into a program supports component- based programming. The programmer should choose syntax Python appropriate for each situation, keeping in mind that the goal of component-based programming is to create pro- grams that are easier to construct and maintain.

4.10 Recursion

The programs we have discussed thus far generally are structured as functions that call one another in a disciplined, hierarchical manner. For some problems, however, it is useful to have functions call themselves. A recursive function is a function that calls itself, either di- rectly or indirectly (through another function). Recursion is an important topic discussed at length in upper-level computer-science courses. In this section and the next, we present simple examples of recursion.

We first consider recursion conceptually and then illustrate several recursive func- tions. Recursive problem-solving approaches have a number of elements in common. A recursive function is called to solve a problem. The function actually knows how to solve only the simplest case(s), or so-called base case(s). If the function is not called with a base case, the function divides the problem into two conceptual pieces—a piece that the function knows how to solve (a base case) and a piece that the function does not know how to solve. To make recursion feasible, the latter piece must resemble the original problem, but be a slightly simpler or slightly smaller version of the original problem. Because this new problem looks like the original problem, the function invokes (calls) a fresh copy of itself to go to work on the smaller problem; this is referred to as a recursive call and is also called the recursion step. The recursion step normally includes the keyword return, because this result will be combined with the portion of the problem the function knew how to solve to form a result that will be passed back to the original caller.

The recursion step executes while the original call to the function is still open (i.e., while it has not finished executing). The recursion step can result in many more such recur- sive calls, as the function divides each new subproblem into two conceptual pieces. For the recursion eventually to terminate, the sequence of smaller and smaller problems must con- verge on a base case. At that point, the function recognizes the base case and returns a result to the previous copy of the function, and a sequence of returns ensues up the line until the original function call eventually returns the final result to the caller. This process sounds exotic when compared with the conventional problem solving techniques we have used to this point. As an example of these concepts at work, let us write a recursive program to per- form a popular mathematical calculation.

The factorial of a nonnegative integer n, written n! (and pronounced “n factorial”), is the product

n · (n - 1) · (n - 2) · … · 1

with 1! equal to 1, and 0! equal to 1. For example, 5! is the product 5 · 4 · 3 · 2 · 1, which is equal to 120.

The factorial of an integer, number, greater than or equal to 0 can be calculated iter- atively (nonrecursively) using for, as follows:

factorial = 1

for counter in range( 1, number + 1 ): factorial *= counter

A recursive definition of the factorial function is obtained by observing the following relationship:

n! = n · (n - 1)!

For example, 5! is clearly equal to 5 * 4!, as is shown by the following equations: 5! = 5 · 4 · 3 · 2 · 1

5! = 5 · (4 · 3 · 2 · 1) 5! = 5 · (4!)

The evaluation of 5! would proceed as shown in Fig. 4.16. Figure 4.16 (a) shows how the succession of recursive calls proceeds until 1! evaluates to 1, which terminates the recursion. Figure 4.16 (b) shows the values returned from each recursive call to its caller until the final value is calculated and returned.

Figure 4.17 uses recursion to calculate and print the factorials of the integers from 0 to 10. The recursive function factorial (lines 5–10) first tests to determine whether a terminating condition is true (line 7)—if number is less than or equal to 1 (the base case),

factorial returns 1, no further recursion is necessary and the function terminates. Oth- erwise, if number is greater than 1, line 10 expresses the problem as the product of

number and a recursive call to factorial evaluating the factorial of number - 1. Note that factorial( number - 1 ) is a simpler version of the original calculation,

factorial( number ).

Common Programming Error 4.7

Either omitting the base case or writing the recursion step incorrectly so that it does not con- verge on the base case will cause infinite recursion, eventually exhausting memory. This is analogous to the problem of an infinite loop in an iterative (nonrecursive) solution. 4.7

Fig. 4.16 Fig. 4.16 Fig. 4.16

Fig. 4.16 Recursive evaluation of 5!. 5! 5 * 4! 4 * 3! 3 * 2! 2 * 1! 1

(a) Procession of recursive calls (b) Values returned from each recursive call 5! 5 * 4! 4 * 3! 3 * 2! 2 * 1! 1 Final value = 120 5! = 5 * 24 = 120 is returned 4! = 4 * 6 = 24 is returned 3! = 3 * 2 = 6 is returned 2! = 2 * 1 = 2 is returned 1 returned

4.11 Example Using Recursion: The Fibonacci Series

In document Python - How to Program (Page 140-143)