The global statement and its nonlocal 3.X cousin are the only things that are remotely like declaration statements in Python. They are not type or size declarations, though; they are namespace declarations. The global statement tells Python that a function plans to change one or more global names—that is, names that live in the enclosing module’s scope (namespace).
We’ve talked about global in passing already. Here’s a summary:
Global names are variables assigned at the top level of the enclosing module file.
Global names must be declared only if they are assigned within a function.
Global names may be referenced within a function without being declared.
In other words, global allows us to change names that live outside a def at the top level of a module file. As we’ll see later, the nonlocal statement is almost identical but applies to names in the enclosing def’s local scope, rather than names in the enclosing module.
The global statement consists of the keyword global, followed by one or more names separated by commas. All the listed names will be mapped to the enclosing module’s scope when assigned or referenced within the function body. For instance:
X = 88 # Global X def func(): global X X = 99 # Global X: outside def func() print(X) # Prints 99
We’ve added a global declaration to the example here, such that the X inside the def now refers to the X outside the def; they are the same variable this time, so changing X inside the function changes the X outside it. Here is a slightly more involved example of global at work:
y, z = 1, 2 # Global variables in module def all_global(): global x # Declare globals assigned x = y + z # No need to declare y, z: LEGB rule
Here, x, y, and z are all globals inside the function all_global. y and z are global because they aren’t assigned in the function; x is global because it was listed in a global statement to map it to the module’s scope explicitly. Without the global here, x would be considered local by virtue of the assignment.
Notice that y and z are not declared global; Python’s LEGB lookup rule finds them in the module automatically. Also, notice that x does not even exist in the enclosing module before the function runs; in this case, the first assignment in the function creates x in the module.
Functions in general, and global variables in particular, raise some larger design questions. How should our functions communicate? Although some of these will become more apparent when you begin writing larger functions of your own, a few guidelines up front might spare you from problems later. In general, functions should rely on arguments and return values instead of globals, but I need to explain why.
By default, names assigned in functions are locals, so if you want to change names outside functions you have to write extra code (e.g., global statements). This is deliberate—as is common in Python, you have to say more to do the potentially “wrong” thing. Although there are times when globals are useful, variables assigned in a def are local by default because that is normally the best policy. Changing globals can lead to well-known software engineering problems: because the variables’ values are dependent on the order of calls to arbitrarily distant functions, programs can become difficult to debug, or to understand at all.
Consider this module file, for example, which is presumably imported and used elsewhere:
X = 99
def func1():
global X
X = 88
def func2():
global X
X = 77
Now, imagine that it is your job to modify or reuse this code. What will the value of X be here? Really, that question has no meaning unless it’s qualified with a point of reference in time—the value of X is timing-dependent, as it depends on which function was called last (something we can’t tell from this file alone).
The net effect is that to understand this code, you have to trace the flow of control through the entire program. And, if you need to reuse or modify the code, you have to keep the entire program in your head all at once. In this case, you can’t really use one of these functions without bringing along the other. They are dependent on—that is, coupled with—the global variable. This is the problem with globals: they generally make code more difficult to understand and reuse than code consisting of self-contained functions that rely on locals.
On the other hand, short of using tools like nested scope closures or object-oriented programming with classes, global variables are probably the most straightforward way in Python to retain shared state information—information that a function needs to remember for use the next time it is called. Local variables disappear when the function returns, but globals do not. As we’ll see later, other techniques can achieve this, too, and allow for multiple copies of the retained information, but they are generally more complex than pushing values out to the global scope for retention in simple use cases where this applies.
Moreover, some programs designate a single module to collect globals; as long as this is expected, it is not as harmful. Programs that use multithreading to do parallel processing in Python also commonly depend on global variables—they become shared memory between functions running in parallel threads, and so act as a communication device.[36]
For now, though, especially if you are relatively new to programming, avoid the temptation to use globals whenever you can—they tend to make programs difficult to understand and reuse, and won’t work for cases where one copy of saved data is not enough. Try to communicate with passed-in arguments and return values instead. Six months from now, both you and your coworkers may be happy you did.
Here’s another scope-related design issue: although we can change variables in another file directly, we usually shouldn’t. Module files were introduced in Chapter 3 and are covered in more depth in the next part of this book. To illustrate their relationship to scopes, consider these two module files:
# first.py X = 99 # This code doesn't know about second.py # second.py import first print(first.X) # OK: references a name in another file first.X = 88 # But changing it can be too subtle and implicit
The first defines a variable X, which the second prints and then changes by assignment. Notice that we must import the first module into the second file to get to its variable at all—as we’ve learned, each module is a self-contained namespace (package of variables), and we must import one module to see inside it from another. That’s the main point about modules: by segregating variables on a per-file basis, they avoid name collisions across files, in much the same way that local variables avoid name clashes across functions.
Really, though, in terms of this chapter’s topic, the global scope of a module file becomes the attribute namespace of the module object once it is imported—importers automatically have access to all of the file’s global variables, because a file’s global scope morphs into an object’s attribute namespace when it is imported.
After importing the first module, the second module prints its variable and then assigns it a new value. Referencing the module’s variable to print it is fine—this is how modules are linked together into a larger system normally. The problem with the assignment to first.X, however, is that it is far too implicit: whoever’s charged with maintaining or reusing the first module probably has no clue that some arbitrarily far-removed module on the import chain can change X out from under him or her at runtime. In fact, the second module may be in a completely different directory, and so difficult to notice at all.
Although such cross-file variable changes are always possible in Python, they are usually much more subtle than you will want. Again, this sets up too strong a coupling between the two files—because they are both dependent on the value of the variable X, it’s difficult to understand or reuse one file without the other. Such implicit cross-file dependencies can lead to inflexible code at best, and outright bugs at worst.
Here again, the best prescription is generally to not do this—the best way to communicate across file boundaries is to call functions, passing in arguments and getting back return values. In this specific case, we would probably be better off coding an accessor function to manage the change:
# first.py X = 99 def setX(new): # Accessor make external changes explit global X # And can manage access in a single place X = new # second.py import first first.setX(88) # Call the function instead of changing directly
This requires more code and may seem like a trivial change, but it makes a huge difference in terms of readability and maintainability—when a person reading the first module by itself sees a function, that person will know that it is a point of interface and will expect the change to the X. In other words, it removes the element of surprise that is rarely a good thing in software projects. Although we cannot prevent cross-file changes from happening, common sense dictates that they should be minimized unless widely accepted across the program.
When we meet classes in Part VI, we’ll see similar techniques for coding attribute accessors. Unlike modules, classes can also intercept attribute fetches automatically with operator overloading, even when accessors aren’t used by their clients.
Interestingly, because global-scope variables morph into the attributes of a loaded module object, we can emulate the global statement by importing the enclosing module and assigning to its attributes, as in the following example module file. Code in this file imports the enclosing module, first by name, and then by indexing the sys.modules loaded modules table (more on this table in Chapter 22 and Chapter 25):
# thismod.py var = 99 # Global variable == module attribute def local(): var = 0 # Change local var def glob1(): global var # Declare global (normal) var += 1 # Change global var def glob2(): var = 0 # Change local var import thismod # Import myself thismod.var += 1 # Change global var def glob3(): var = 0 # Change local var import sys # Import system table glob = sys.modules['thismod'] # Get module object (or use __name__) glob.var += 1 # Change global var def test(): print(var) local(); glob1(); glob2(); glob3() print(var)
When run, this adds 3 to the global variable (only the first function does not impact it):
>>>import thismod>>>thismod.test()99 102 >>>thismod.var102
This works, and it illustrates the equivalence of globals to module attributes, but it’s much more work than using the global statement to make your intentions explicit.
As we’ve seen, global allows us to change names in a module outside a function. It has a close relative named nonlocal that can be used to change names in enclosing functions, too—but to understand how that can be useful, we first need to explore enclosing functions in general.
[36] Multithreading runs function calls in parallel with the rest of the program and is supported by Python’s standard library modules _thread, threading, and queue (thread, threading, and Queue in Python 2.X). Because all threaded functions run in the same process, global scopes often serve as one form of shared memory between them (threads may share both names in global scopes, as well as objects in a process’s memory space). Threading is commonly used for long-running tasks in GUIs, to implement nonblocking operations in general and to maximize CPU capacity. It is also beyond this book’s scope; see the Python library manual, as well as the follow-up texts listed in the preface (such as O’Reilly’s Programming Python), for more details.