Does Python provide a way to access a variable beyond the nearest enclosing scope?

Viewed 61

Given the example below, inspired by another from a SO Question:

x = 0
def func1():
    x = 1
    def func2():
        x = 2
        def func3():
            KEYWORD x # scope declaration
            x = 3
        func3()
    func2()
func1()

With global and nonlocal as KEYWORD, For x the values are respectively 0 and 2 instead of 3.

Does Python provides a mechanism to use the scope of func1() for x in func3()? Or only global, func2 and func3 scopes are accessible here?

1 Answers

No, you cannot specify a specific scope with nonlocal. It will always choose the nearest enclosing scope that contains the named variable.

Since Python uses lexical scoping, the only way to get into this situation is by writing the code for all the scopes yourself. (E.g., when you write func3, you know it is nested in func2, so you know all the variables in scope in func2.) Just don't reuse the same names in each scope when you write the code, and nonlocal will do the right thing. (We'll ignore the issue of writing such deeply nested code in the first place.)

x_global = 0
def func1():
    x_first = 1
    def func2():
        x_second = 2
        def func3():
            nonlocal x_first # scope declaration
            x_first = 3
        func3()
    func2()
func1()

In contrast, let's pretend Python used dynamic scoping.

x = 9

def func1():
    nonlocal x
    x = 3

def func2():
    x = 1
    func1()
    print(x)

def func3():
    print(x)

func2()    # Prints 3
print(x)   # Prints 9; the global variable hasn't been changed
func1()
print(x)   # Prints 3; the global variable *has* changed.

The variable modified by func1 now depends on the scope in which func1 is called.

In real Python, the above sequence would print

1
3
3

as func1 will alway modify the global variable, whether it is called from the global scope or from func2's local scope.

Related