Scopes and Nested Functions

So far, I’ve omitted one part of Python’s scope rules on purpose, because it’s relatively uncommon to encounter it in practice. However, it’s time to take a deeper look at the letter E in the LEGB lookup rule. The E layer was added in Python 2.2; it takes the form of the local scopes of any and all enclosing function’s local scopes. Enclosing scopes are sometimes also called statically nested scopes. Really, the nesting is a lexical one—nested scopes correspond to physically and syntactically nested code structures in your program’s source code text.

Nested Scope Details

With the addition of nested function scopes, variable lookup rules become slightly more complex. Within a function:

  • A reference (X) looks for the name X first in the current local scope (function); then in the local scopes of any lexically enclosing functions in your source code, from inner to outer; then in the current global scope (the module file); and finally in the built-in scope (the module builtins). global declarations make the search begin in the global (module file) scope instead.

  • An assignment (X = value) creates or changes the name X in the current local scope, by default. If X is declared global within the function, the assignment creates or changes the name X in the enclosing module’s scope instead. If, on the other hand, X is declared nonlocal within the function in 3.X (only), the assignment changes the name X in the closest enclosing function’s local scope.

Notice that the global declaration still maps variables to the enclosing module. When nested functions are present, variables in enclosing functions may be referenced, but they require 3.X nonlocal declarations to be changed.

Nested Scope Examples

To clarify the prior section’s points, let’s illustrate with some real code. Here is what an enclosing function scope looks like (type this into a script file or at the interactive prompt to run it live):

X = 99                   # Global scope name: not used

def f1():
    X = 88               # Enclosing def local
    def f2():
        print(X)         # Reference made in nested def
    f2()

f1()                     # Prints 88: enclosing def local

First off, this is legal Python code: the def is simply an executable statement, which can appear anywhere any other statement can—including nested in another def. Here, the nested def runs while a call to the function f1 is running; it generates a function and assigns it to the name f2, a local variable within f1’s local scope. In a sense, f2 is a temporary function that lives only during the execution of (and is visible only to code in) the enclosing f1.

But notice what happens inside f2: when it prints the variable X, it refers to the X that lives in the enclosing f1 function’s local scope. Because functions can access names in all physically enclosing def statements, the X in f2 is automatically mapped to the X in f1, by the LEGB lookup rule.

This enclosing scope lookup works even if the enclosing function has already returned. For example, the following code defines a function that makes and returns another function, and represents a more common usage pattern:

def f1():
    X = 88
    def f2():
        print(X)         # Remembers X in enclosing def scope
    return f2            # Return f2 but don't call it

action = f1()            # Make, return function
action()                 # Call it now: prints 88

In this code, the call to action is really running the function we named f2 when f1 ran. This works because functions are objects in Python like everything else, and can be passed back as return values from other functions. Most importantly, f2 remembers the enclosing scope’s X in f1, even though f1 is no longer active—which leads us to the next topic.

Factory Functions: Closures

Depending on whom you ask, this sort of behavior is also sometimes called a closure or a factory function—the former describing a functional programming technique, and the latter denoting a design pattern. Whatever the label, the function object in question remembers values in enclosing scopes regardless of whether those scopes are still present in memory. In effect, they have attached packets of memory (a.k.a. state retention), which are local to each copy of the nested function created, and often provide a simple alternative to classes in this role.

A simple function factory

Factory functions (a.k.a. closures) are sometimes used by programs that need to generate event handlers on the fly in response to conditions at runtime. For instance, imagine a GUI that must define actions according to user inputs that cannot be anticipated when the GUI is built. In such cases, we need a function that creates and returns another function, with information that may vary per function made.

To illustrate this in simple terms, consider the following function, typed at the interactive prompt (and shown here without the “...” continuation-line prompts, per the presentation note ahead):

>>> def maker(N):
        def action(X):                    # Make and return action
            return X ** N                 # action retains N from enclosing scope
        return action

This defines an outer function that simply generates and returns a nested function, without calling it—maker makes action, but simply returns action without running it. If we call the outer function:

>>> f = maker(2)                          # Pass 2 to argument N
>>> f
<function maker.<locals>.action at 0x0000000002A4A158>

what we get back is a reference to the generated nested function—the one created when the nested def runs. If we now call what we got back from the outer function:

>>> f(3)                                  # Pass 3 to X, N remembers 2: 3 ** 2
9
>>> f(4)                                  # 4 ** 2
16

we invoke the nested function—the one called action within maker. In other words, we’re calling the nested function that maker created and passed back.

Perhaps the most unusual part of this, though, is that the nested function remembers integer 2, the value of the variable N in maker, even though maker has returned and exited by the time we call action. In effect, N from the enclosing local scope is retained as state information attached to the generated action, which is why we get back its argument squared when it is later called.

Just as important, if we now call the outer function again, we get back a new nested function with different state information attached. That is, we get the argument cubed instead of squared when calling the new function, but the original still squares as before:

>>> g = maker(3)                          # g remembers 3, f remembers 2
>>> g(4)                                  # 4 ** 3
64
>>> f(4)                                  # 4 ** 2
16

This works because each call to a factory function like this gets its own set of state information. In our case, the function we assign to name g remembers 3, and f remembers 2, because each has its own state information retained by the variable N in maker.

This is a somewhat advanced technique that you may not see very often in most code, and may be popular among programmers with backgrounds in functional programming languages. On the other hand, enclosing scopes are often employed by the lambda function-creation expressions we’ll expand on later in this chapter—because they are expressions, they are almost always nested within a def. For example, a lambda would serve in place of a def in our example:

>>> def maker(N):
        return lambda X: X ** N           # lambda functions retain state too

>>> h = maker(3)
>>> h(4)                                  # 4 ** 3 again
64

For a more tangible example of closures at work, see the upcoming sidebar Why You Will Care: Customizing open. It uses similar techniques to store information for later use in an enclosing scope.

Note

Presentation note: In this chapter, I’ve started listing interactive examples without the “...” continuation-line prompts that may or may not appear in your interface (they do at the shell, but not in IDLE). This convention will be followed from this point on to make larger code examples a bit easier to cut and paste from an ebook or other. I’m assuming that by now you understand indentation rules and have had your fair share of typing Python code, and some functions and classes ahead may be too large for rote input.

I’m also listing more and more code alone or in files, and switching between these and interactive input arbitrarily; when you see a “>>>” prompt, the code is typed interactively, and can generally be cut and pasted into your Python shell if you omit the “>>>” itself. If this fails, you can still run by pasting line by line, or editing in a file.

Closures versus classes, round 1

To some, classes, described in full in Part VI of this book, may seem better at state retention like this, because they make their memory more explicit with attribute assignments. Classes also directly support additional tools that closure functions do not, such as customization by inheritance and operator overloading, and more naturally implement multiple behaviors in the form of methods. Because of such distinctions, classes may be better at implementing more complete objects.

Still, closure functions often provide a lighter-weight and viable alternative when retaining state is the only goal. They provide for per-call localized storage for data required by a single nested function. This is especially true when we add the 3.X nonlocal statement described ahead to allow enclosing scope state changes (in 2.X, enclosing scopes are read-only, and so have more limited uses).

From a broader perspective, there are multiple ways for Python functions to retain state between calls. Although the values of normal local variables go away when a function returns, values can be retained from call to call in global variables; in class instance attributes; in the enclosing scope references we’ve met here; and in argument defaults and function attributes. Some might include mutable default arguments to this list too (though others may wish they didn’t).

We’ll preview class-based alternatives and meet function attributes later in this chapter, and get the full story on arguments and defaults in Chapter 18. To help us judge how defaults compete on state retention, though, the next section gives enough of an introduction to get us started.

Note

Closures can also be created when a class is nested in a def: the values of the enclosing function’s local names are retained by references within the class, or one of its method functions. See Chapter 29 for more on nested classes. As we’ll see in later examples (e.g., Chapter 39’s decorators), the outer def in such code serves a similar role: it becomes a class factory, and provides state retention for the nested class.

Retaining Enclosing Scope State with Defaults

In early versions of Python (prior to 2.2), the sort of code in the prior section failed because nested defs did not do anything about scopes—a reference to a variable within f2 in the following would search only the local (f2), then global (the code outside f1), and then built-in scopes. Because it skipped the scopes of enclosing functions, an error would result. To work around this, programmers typically used default argument values to pass in and remember the objects in an enclosing scope:

def f1():
    x = 88
    def f2(x=x):                # Remember enclosing scope X with defaults
        print(x)
    f2()

f1()                            # Prints 88

This coding style works in all Python releases, and you’ll still see this pattern in some existing Python code. In fact, it’s still required for loop variables, as we’ll see in a moment, which is why it remains worth studying today. In short, the syntax arg=val in a def header means that the argument arg will default to the value val if no real value is passed to arg in a call. This syntax is used here to explicitly assign enclosing scope state to be retained.

Specifically, in the modified f2 here, the x=x means that the argument x will default to the value of x in the enclosing scope—because the second x is evaluated before Python steps into the nested def, it still refers to the x in f1. In effect, the default argument remembers what x was in f1: the object 88.

That’s fairly complex, and it depends entirely on the timing of default value evaluations. In fact, the nested scope lookup rule was added to Python to make defaults unnecessary for this role—today, Python automatically remembers any values required in the enclosing scope for use in nested defs.

Of course, the best prescription for much code is simply to avoid nesting defs within defs, as it will make your programs much simpler—in the Pythonic view, flat is generally better than nested. The following is an equivalent of the prior example that avoids nesting altogether. Notice the forward reference in this code—it’s OK to call a function defined after the function that calls it, as long as the second def runs before the first function is actually called. Code inside a def is never evaluated until the function is actually called:

>>> def f1():
        x = 88                  # Pass x along instead of nesting
        f2(x)                   # Forward reference OK

>>> def f2(x):
        print(x)                # Flat is still often better than nested!

>>> f1()
88

If you avoid nesting this way, you can almost forget about the nested scopes concept in Python. On the other hand, the nested functions of closure (factory) functions are fairly common in modern Python code, as are lambda functions—which almost naturally appear nested in defs and often rely on the nested scopes layer, as the next section explains.

Nested scopes, defaults, and lambdas

Although they see increasing use in defs these days, you may be more likely to care about nested function scopes when you start coding or reading lambda expressions. We’ve met lambda briefly and won’t cover it in depth until Chapter 19, but in short, it’s an expression that generates a new function to be called later, much like a def statement. Because it’s an expression, though, it can be used in places that def cannot, such as within list and dictionary literals.

Like a def, a lambda expression also introduces a new local scope for the function it creates. Thanks to the enclosing scopes lookup layer, lambdas can see all the variables that live in the functions in which they are coded. Thus, the following code—a variation on the factory we saw earlier—works, but only because the nested scope rules are applied:

def func():
    x = 4
    action = (lambda n: x ** n)          # x remembered from enclosing def
    return action

x = func()
print(x(2))                              # Prints 16, 4 ** 2

Prior to the introduction of nested function scopes, programmers used defaults to pass values from an enclosing scope into lambdas, just as for defs. For instance, the following works on all Pythons:

def func():
    x = 4
    action = (lambda n, x=x: x ** n)     # Pass x in manually
    return action

Because lambdas are expressions, they naturally (and even normally) nest inside enclosing defs. Hence, they were perhaps the biggest initial beneficiaries of the addition of enclosing function scopes in the lookup rules; in most cases, it is no longer necessary to pass values into lambdas with defaults.

Loop variables may require defaults, not scopes

There is one notable exception to the rule I just gave (and a reason why I’ve shown you the otherwise dated default argument technique we just saw): if a lambda or def defined within a function is nested inside a loop, and the nested function references an enclosing scope variable that is changed by that loop, all functions generated within the loop will have the same value—the value the referenced variable had in the last loop iteration. In such cases, you must still use defaults to save the variable’s current value instead.

This may seem a fairly obscure case, but it can come up in practice more often than you may think, especially in code that generates callback handler functions for a number of widgets in a GUI—for instance, handlers for button-clicks for all the buttons in a row. If these are created in a loop, you may need to be careful to save state with defaults, or all your buttons’ callbacks may wind up doing the same thing.

Here’s an illustration of this phenomenon reduced to simple code: the following attempts to build up a list of functions that each remember the current variable i from the enclosing scope:

>>> def makeActions():
        acts = []
        for i in range(5):                       # Tries to remember each i
            acts.append(lambda x: i ** x)        # But all remember same last i!
        return acts

>>> acts = makeActions()
>>> acts[0]
<function makeActions.<locals>.<lambda> at 0x0000000002A4A400>

This doesn’t quite work, though—because the enclosing scope variable is looked up when the nested functions are later called, they all effectively remember the same value: the value the loop variable had on the last loop iteration. That is, when we pass a power argument of 2 in each of the following calls, we get back 4 to the power of 2 for each function in the list, because i is the same in all of them—4:

>>> acts[0](2)                                   # All are 4 ** 2, 4=value of last i
16
>>> acts[1](2)                                   # This should be 1 ** 2 (1)
16
>>> acts[2](2)                                   # This should be 2 ** 2 (4)
16
>>> acts[4](2)                                   # Only this should be 4 ** 2 (16)
16

This is the one case where we still have to explicitly retain enclosing scope values with default arguments, rather than enclosing scope references. That is, to make this sort of code work, we must pass in the current value of the enclosing scope’s variable with a default. Because defaults are evaluated when the nested function is created (not when it’s later called), each remembers its own value for i:

>>> def makeActions():
        acts = []
        for i in range(5):                       # Use defaults instead
            acts.append(lambda x, i=i: i ** x)   # Remember current i
        return acts

>>> acts = makeActions()
>>> acts[0](2)                                   # 0 ** 2
0
>>> acts[1](2)                                   # 1 ** 2
1
>>> acts[2](2)                                   # 2 ** 2
4
>>> acts[4](2)                                   # 4 ** 2
16

This seems an implementation artifact that is prone to change, and may become more important as you start writing larger programs. We’ll talk more about defaults in Chapter 18 and lambdas in Chapter 19, so you may also want to return and review this section later.[37]

Arbitrary scope nesting

Before ending this discussion, we should note that scopes may nest arbitrarily, but only enclosing function def statements (not classes, described in Part VI) are searched when names are referenced:

>>> def f1():
        x = 99
        def f2():
            def f3():
                print(x)        # Found in f1's local scope!
            f3()
        f2()

>>> f1()
99

Python will search the local scopes of all enclosing defs, from inner to outer, after the referencing function’s local scope and before the module’s global scope or built-ins. However, this sort of code is even less likely to pop up in practice. Again, in Python, we say flat is better than nested, and this still holds generally true even with the addition of nested scope closures. Except in limited contexts, your life (and the lives of your coworkers) will generally be better if you minimize nested function definitions.



[37] In the section Function Gotchas, we’ll also see that there is a similar issue with using mutable objects like lists and dictionaries for default arguments (e.g., def f(a=[]))—because defaults are implemented as single objects attached to functions, mutable defaults retain state from call to call, rather then being initialized anew on each call. Depending on whom you ask, this is either considered a feature that supports another way to implement state retention, or a strange corner of the language; more on this at the end of Chapter 21.