• Nenhum resultado encontrado

Python 2 versus Python 3

E.4 SCOPE

though we did not specify either when writing the functions! How can you predict what will happen when you write a function? The key is to remember that a variablepoints toan object, but it is not the object. Technically, arguments to functions are always passed by value in Python. However, the

“value” being passed is a reference to an object, an address in memory. Thus, a function receives acopy of the memory address where an argument’s object is stored. It creates a new local variable for each argument and binds it to the same object as the corresponding argument. A functionf(x)cannot modify this address and make x point to adifferent object, but it can use this address to access methods of the object stored there, and thereby modify the object that xpoints to. To distinguish this behavior from the ordinary usage of pass by value and pass by reference, the paradigm used in Python is often referred to asreference passed by value,orpass by address.

To better understand the behavior of functions, we next need to explore how Python finds an object when it encounters a variable name.

E.4 SCOPE

The way in which Python looks up the value of a variable is subtle but important. Python keeps track of variables using namespaces. A namespaceis like a directory of names and objects that tells Python where to find the object associated with a variable name. The subtle point is that Python maintains multiple namespaces, each with its own scope. A scope is a portion of a program, like a function body or a module, in which a namespace is accessible. For example, there is a namespace that keeps track of all the variables you define at the command prompt. Its scope is global: Any script you run or commands you type at the command prompt can find and use these variables.

Each time Python executes a function, it creates alocalnamespace containing all variables created within the function. They are concealed from everything outside because the scope of the local namespace is limited to the function itself. Recall thetaxicab function of the measurements.py module in Section 6.1:

# excerpt from measurements.py [get code]

def taxicab(pointA, pointB):

"""

Taxicab metric for computing distance between points A and B.

5 pointA = (x1, y1)

pointB = (x2, y2)

Returns |x2-x1| + |y2-y1|. Distances are measured in city blocks.

"""

interval = abs(pointB[0] - pointA[0]) + abs(pointB[1] - pointA[1])

10 return interval

There is no variable calledintervalin the Variable Explorer before or after you execute thetaxicab function. It only exists in the local namespace of the function.

When you refer to a variable from within a function, Python will look for an object with that name in an expanding search. Suppose that you accidentally typedx1instead ofpointA[0]in the body oftaxicab. When you call the function, Python will search for the namex1 in the following namespaces, in order:

Local: First, Python determines whetherx1is defined in the function body or in its argument list.

If so, it ends the search and uses the object bound to that variable. There is no variable called x1in this example, so Python expands the search.

Jump to Contents Jump to Index

138 Appendix E Under the Hood

Enclosing: Next, Python will determine whether the current function is defined as part of another function. If so, it will determine whetherx1was defined within or passed as an argument to this or any otherenclosing functions. There may be multiple nested functions, each with its own namespace for Python to search.

Global: If it still has not found x1, Python will check to see whether the module in which the function is defined contains a variable called x1. (For example, if you had added the line x1=10outside of any function definitions in themeasurements.pymodule, Python would stop searching and use this value. If you definedtaxicabat the command prompt or by running a script instead of importing the module, then Python would search among all the variables in your current session.)

Built-in: As a last resort, Python will check its built-in functions and parameters—those listed in dir(_ _builtins_ _). If it cannot findx1here, it gives up and raises aNameErrorexception.

Knowing this hierarchy of L→E→G→B can be useful in debugging. Python may be finding a value for a variable somewhere you never intended.

The following function demonstrates Python’s rules of scope.

# scope.py [get code]

def illustrate_scope():

s_enclosing = 'E' def display():

5 s_local = 'L'

print("Local --> {}".format(s_local))

print("Enclosing --> {}".format(s_enclosing)) print("Global --> {}".format(s_global)) print("Built-in --> {}".format(abs))

10 display() s_global = 'G' illustrate_scope()

This script defines three variables whose names indicate their namespaces with respect to the display() function. Whenillustrate_scope() is called in the final line, Python executes the function body. It defines a variable in the current namespace, defines the display() function, and then calls this newly defined function. When Python executes display(), it must search for four different names. It findss_local within the local namespace, but none of the other variables are defined there. Python finds s_enclosingin the enclosing namespace,s_globalin the global namespace, andabs within Python’s collection of built-in functions.

E.4.1 Name collisions

Scope is important in determining the effect of a function call. In the preceding example, all of the variables had unique names. But what happens when variables in different namespaces have the same name? Try the following example to see how Python resolves name collisions.

E.4 Scope 139

# name_collision.py [get code]

def name_collisions():

x, y = 'E', 'E' def display():

5 x = 'L'

print("Inside display() ...")

print("x= {}\ny= {}\nz= {}".format(x, y, z)) display()

print("Inside name_collision() ...")

10 print("x= {}\ny= {}\nz= {}".format(x, y, z))

x, y, z = 'G', 'G', 'G' name_collisions()

print("Outside function ...")

15 print("x= {}\ny= {}\nz= {}".format(x, y, z))

Each assignment statement creates a variable in the current namespace, but it has no effect on the variables in other namespaces. Thus, in this example there are three variables with the name x, two with the name y, and one with the name z. When Python executes name_collision() anddisplay(), it must determine a value for each variable. It starts searching in the innermost namespace, works its way through the L→E→G→B hierarchy, and uses the value from the first namespace in which it locates each variable name.

Python uses namespaces, each with its own scope, to protect programs from unintended name collisions. This means that you do not have to worry about giving each variable a globally unique name when writing your own functions. However, Python cannot help you if you use the same variable name for two different purposes in the same namespace, as discussed in Section 1.4.3. Reassigning a name destroys any previous connection to another object. (Python’s namespaces do not include the mind of the programmer!) Modular programming—breaking complex programs into a series of simple functions that each accomplish one thing—and descriptive variable names are the best ways to prevent this type of “name collision.”

E.4.2 Variables passed as arguments

When a variable is passed to a function as an argument, Python creates a new variable in the function’s local namespace and binds it to the same object as the argument. Despite referring to the same object, these two variables are independent—even if they have thesame name. They exist in different namespaces.

If an assignment statement within a function binds an argument to a new value, there is no effect on the global variable passed as that argument. Python simply assigns the local variable to a different object. In this way, Python allows functions to access external variables, not modify them. Such modularity enforces good coding practice; however, there is an important exception to this principle.

A functioncan modify an object using themethods of that object. The more precise rule is, “A function cannot reassign an external variable.” If a function modifies an argument using a method of that argument, it is modifying an object that is bound to variables in multiple namespaces. This will result in side effects—whether by design or mistake.

We can now explain the behavior of object_plus_one(x) and elements_plus_one(x) in Section E.3. Whenxis passed as an argument to the function, a new local variable yis created in Jump to Contents Jump to Index

140 Appendix E Under the Hood

the function’s local namespace and bound to the same array asx, as in Figure E.1b. The assignment y=y+1 binds the localyto a new object with no effect on the globalx, as in Figure E.1c. Because no value is returned by the function, and Python deletes local variables after evaluating the function, object_plus_one(x) has no external effects—it accomplishes nothing.

In contrast, elements_plus_one(x) does not reassign its local variable y to a new object.

It uses an array method to modify the data of y. (See Section 2.2.6, page 24.) The statement y[0]=y[0]+1changes the value of the first element of the array, as shown in Figure E.1d. Because the variableyin the function’s local namespace and the variablexin the global namespace point to thesame array,x[0]is also changed. This continues as the function iterates over the loop. Unlike object_plus_one(x),elements_plus_one(x)produces a side effect: It changes the value ofx.