Python for Programmers: Nonlocal and Global Scope
Welcome to the Nonlocal and Global Scope lesson!
This lesson is shown as static text below. However, it's designed to be used interactively. Click the button below to start!
Python gives us fine-grained control over global and "nonlocal" variable access, which is unusual among programming languages. We'll briefly look at Python's sensible scoping rules, which match most languages. Then we'll see
globalandnonlocal, which let us control variable scope.Local variables "shadow" global variables. If a local and global variable both have the name
x, we can only access the local variablex. The global variablexis shadowed, and inaccessible, since we can only have one variable namedxin a given scope.>
x = 5def increment(initial_value):x = initial_valuex = x + 1return xIn the example above,
x = initial_valuecreates a new local variable inside the function. This localxshadows the globalx. Note that it doesn't modify the globalx!- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
incremented_x = increment(10)incremented_xResult:
11
- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
incremented_x = increment(10)xResult:
5
Sometimes we want more fine-grained control over how scoping works. For example, what if we really do want the above function to modify the global variable
x?In Python, we can use the
globalstatement to explicitly say "xin this function refers to the globalx, not a local variablex". Here's the same code example again. The only difference is that we've addedglobal. Now, callingincrementmodifies the globalx. That global change is visible even afterincrementreturns.>
x = 5def increment(initial_value):global xx = initial_valuex = x + 1return x- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
incremented_x = increment(10)incremented_xResult:
11
- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
incremented_x = increment(10)xResult:
11
There's more to scope than just locals and globals. We might have multiple nested scopes, like when we define a function inside of another function.
The next example tries to create a stateful counter. When we do
increment = create_counter(0), we get a new function stored inincrement. When we later call thatincrementfunction, it increments its value, then returns that value to us.>
def create_counter(initial_value):x = initial_valuedef increment():x = x + 1return xreturn incrementNote the two different
xvariables: one in the outer function, and one in the inner function. That code would work in many languages, but it doesn't work in Python.- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
increment = create_counter(0)increment()Result:
The problem is in the line
x = x + 1. Because we havex = ...in the function, references toxare treated as a reference to a local variablex. The linex = x + 1tries to read from that local variable. But this is the first line in the function, soxhas no value to read.When we actually want to modify a variable in an outer scope, we have to explicitly indicate that with the
nonlocalstatement. The variable is "non-local" because it's not in the current function's local scope, but it's also not in the global scope. We can think of it as being between local and global.Here's the same
create_counterfunction, but with thenonlocalstatement added.>
def create_counter(initial_value):x = initial_valuedef increment():nonlocal xx = x + 1return xreturn incrementincrement = create_counter(0)Now our counter works as intended. When we call it, it increments the value and returns the result.
- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
increment()Result:
1
- Note: this code example reuses elements (variables, etc.) defined in earlier examples.
>
increment()Result:
2
What if we want to create a new local variable, but initialize it from a value in the outer scope? We already saw that we can't read
xfrom the outer scope, then assign to a newxin the local scope. However, there's an easy workaround: just use a different name in the inner scope!>
x = 1def f():inner_x = x + 1return inner_xnew_x = f()[new_x, x]Result:
[2, 1]
Why does Python have both
globalandnonlocal? Don't they mean the same thing?They both refer to variables outside of the current local scope. But
nonlocalwon't let us access global variables. It only works for variables defined in another non-global scope, like the nested functions that we saw above. Trying to access a global withnonlocalcauses an error.>
x = 5def increment(initial_value):nonlocal xx = initial_valuex = x + 1return xincremented_x = increment(10)Result:
SyntaxError: no binding for nonlocal 'x' found (<string>, line 4)
Inversely,
globalonly refers to values at the top level of the current module. Other names that might be visible from outer scopes are not considered.In the next example, the
z = 3line modifies the global variablez.>
z = 1def level_1():z = 2def level_2():global zz = 3level_2()level_1()zResult:
3
Here's a code problem:
Write a function that returns a boolean indicating whether
provided_pinmatchescorrect_pin. If the PIN doesn't match, increment the global variablefailed_attemptsby 1.failed_attempts = 0def check_pin(correct_pin, provided_pin):global failed_attemptsif correct_pin == provided_pin:return Trueelse:failed_attempts += 1return Falsecheck_1 = check_pin("1234", "5678")check_2 = check_pin("9000", "9000")check_3 = check_pin("867", "5309")[check_1, check_2, check_3, failed_attempts]- Goal:
[False, True, False, 2]
- Yours:
[False, True, False, 2]