Function Scope
From other languages we know that a function creates a scope, where the variables inside a function are not accessed from the outside world.
Scope Search
Given the following code, when we try to access the variable x, Python tries to search for it in the current scope. When it fails to find it, it searches in the upper scope until it goes to the global scope, finds it then uses it:
x = 1
def test(): y = 2 print(x)
test() # 1Nested Functions
This also works for nested functions:
x = 1
def f1(): y = 2
def f2(): print(x)
f2()
f1() # 1Due to this scope search behavior, we could shadow a global variable by introducing a variable with the same name in the local scope.
In this example, when we search for x in f2(), Python first looks in the local scope of f2(). Not finding it there, it looks in the enclosing scope (the local scope of f1()), finds x = 2, and uses that value:
x = 1
def f1(): x = 2
def f2(): print(x)
f2()
f1() # 2The global Keyword
How can we change the value of a global variable if when we write x = 3, it creates a new variable in the local scope?
For that we have to use the global keyword:
x = 1
def f1(): global x x = 2
def f2(): print(x)
f2()
f1() # 2print(x) # 2global x tells Python to use the global variable x instead of creating a new local variable.
The nonlocal Keyword
What if we had a variable in the global and local scope with the same name, and we need the inner nested function to change the local value?
x = 1
def f1(): x = 2
def f2(): # Update the local variable's value x = 3 print(x)
f2()
f1() # 3To tell Python that this variable is not really a local variable and our intent is to reference an earlier created variable, we use the nonlocal keyword:
x = 1
def f1(): x = 2
def f2(): nonlocal x x = 3 print(x)
f2()
f1() # 3nonlocal x tells Python to use the local variable x instead of creating a new local variable.
Exercises
Exercise 1: Scope Search
What will be printed by the following code? Explain the scope search process.
x = 10
def outer(): y = 20 def inner(): print(x) print(y) inner()
outer()1020Python searches for x in inner’s local scope, then outer’s scope, then finds it in global scope. For y, it finds it in outer’s (enclosing) scope.
Exercise 2: Shadowing
What will be printed by the following code?
x = 1
def f1(): x = 2 def f2(): print(x) f2() print(x)
f1()print(x)221The local x in f1() shadows the global x. f2() accesses the enclosing scope’s x (which is 2). The global x remains 1.
Exercise 3: Global Keyword
Write code that modifies a global variable counter inside a function. Start with counter = 0, read the number of times to increment, then increment it that many times in a function using the global keyword.
Global Keyword
counter = 0
def increment(): global counter counter += 1
n = int(input())for i in range(n): increment()
print(counter)Exercise 4: Nonlocal Keyword
Write a nested function where the inner function modifies a variable from the enclosing scope using nonlocal. The outer function should have count = 0, and the inner function should increment it.
def outer(): count = 0 def inner(): nonlocal count count += 1 print(count) inner() inner() return count
result = outer() # Prints 1, then 2print(result) # 2Exercise 5: Scope Error
Why does the following code raise an error? How would you fix it?
x = 5
def test(): print(x) x = 10
test()It raises UnboundLocalError because Python sees x = 10 and assumes x is local, but tries to print it before it’s assigned. Fix by using global:
x = 5
def test(): global x print(x) # 5 x = 10
test()print(x) # 10