Practice Questions — Scope, LEGB Rule, and Closures
← Back to NotesTopic-Specific Questions
Question 1
Easy
What is the output of the following code?
x = 10
def show():
print(x)
show()The function can read global variables without the global keyword.
10Question 2
Easy
What is the output?
x = 5
def func():
x = 10
print(x)
func()
print(x)Assignment inside a function creates a local variable.
105Question 3
Easy
What is the output?
def func():
y = 20
print(y)
func()
try:
print(y)
except NameError as e:
print(f"Error: {e}")Local variables do not exist outside the function.
20Error: name 'y' is not definedQuestion 4
Easy
What is the output?
x = "global"
def outer():
x = "enclosing"
def inner():
print(x)
inner()
outer()inner() has no local x, so LEGB search finds the enclosing one.
enclosingQuestion 5
Easy
What is the output?
count = 0
def increment():
global count
count += 1
increment()
increment()
increment()
print(count)The global keyword allows modifying the module-level variable.
3Question 6
Medium
What is the output?
x = 1
def func():
x = 2
print("Inside:", x)
func()
print("Outside:", x)Assignment inside a function creates a NEW local variable, it does not modify the global.
Inside: 2Outside: 1Question 7
Medium
What is the output?
def outer():
x = 10
def inner():
nonlocal x
x += 5
inner()
print(x)
outer()nonlocal allows the inner function to modify the enclosing variable.
15Question 8
Medium
What is the output?
def multiplier(factor):
def multiply(x):
return x * factor
return multiply
double = multiplier(2)
triple = multiplier(3)
print(double(5))
print(triple(5))
print(double(10))Each call to multiplier creates a new closure with its own factor.
101520Question 9
Medium
What is the output?
x = "global"
def outer():
x = "enclosing"
def inner():
x = "local"
print("inner:", x)
inner()
print("outer:", x)
outer()
print("global:", x)Each scope has its own x. Assignment creates a local variable.
inner: localouter: enclosingglobal: globalQuestion 10
Medium
What is the output?
def make_adder(n):
def add(x):
return x + n
return add
add5 = make_adder(5)
add10 = make_adder(10)
print(add5(3))
print(add10(3))
print(add5(add10(1)))Each closure remembers its own n value.
81316Question 11
Hard
What is the output?
x = 10
def func():
try:
print(x)
x = 20
except UnboundLocalError:
print("UnboundLocalError caught")
func()
print(x)The assignment x = 20 makes x local for the ENTIRE function, even the print before it.
UnboundLocalError caught10Question 12
Hard
What is the output?
def counter():
count = 0
def inc():
nonlocal count
count += 1
return count
def get():
return count
return inc, get
inc, get = counter()
print(inc())
print(inc())
print(get())
print(inc())
print(get())Both inc and get share the same enclosing count variable.
12233Question 13
Hard
What is the output?
funcs = []
for i in range(4):
def f():
return i
funcs.append(f)
print([fn() for fn in funcs])All closures capture the same variable i, not its value at creation time.
[3, 3, 3, 3]Question 14
Hard
What is the output?
funcs = []
for i in range(4):
def f(i=i):
return i
funcs.append(f)
print([fn() for fn in funcs])Default parameter i=i captures the current value of i at each iteration.
[0, 1, 2, 3]Question 15
Hard
What is the output?
a = 1
def func():
a = 2
def inner():
global a
a = 3
inner()
print("func:", a)
func()
print("global:", a)inner() uses global, not nonlocal. It modifies the global a, not the enclosing a.
func: 2global: 3Question 16
Hard
What is the output?
def outer():
x = 10
def middle():
def inner():
nonlocal x
x += 1
inner()
middle()
return x
print(outer())nonlocal searches upward through enclosing scopes. inner has no x, middle has no x, outer has x.
11Mixed & Application Questions
Question 1
Easy
What is the output?
name = "Aarav"
def greet():
print(f"Hello, {name}")
greet()
name = "Priya"
greet()The function reads the global variable at call time, not definition time.
Hello, AaravHello, PriyaQuestion 2
Easy
What is the output?
for i in range(5):
pass
print(i)Python for loops do not create a new scope. The loop variable persists.
4Question 3
Easy
What is the output?
def func(a, b):
c = a + b
return c
result = func(3, 4)
print(result)
try:
print(c)
except NameError:
print("c is not defined")c is a local variable inside func. It does not exist outside.
7c is not definedQuestion 4
Medium
What is the output?
def power_factory(exp):
def power(base):
return base ** exp
return power
square = power_factory(2)
cube = power_factory(3)
print(square(4))
print(cube(4))Each closure captures a different value of exp.
1664Question 5
Medium
What is the output?
x = [1, 2, 3]
def modify(lst):
lst.append(4)
modify(x)
print(x)Lists are mutable. The function modifies the same object, no global keyword needed.
[1, 2, 3, 4]Question 6
Medium
What is the output?
x = [1, 2, 3]
def replace(lst):
lst = [4, 5, 6]
print("Inside:", lst)
replace(x)
print("Outside:", x)Reassigning the parameter creates a new local reference, not modifying the original.
Inside: [4, 5, 6]Outside: [1, 2, 3]Question 7
Medium
What is the LEGB rule? Explain each letter.
It is the order Python searches for variable names.
LEGB stands for: Local (inside the current function), Enclosing (outer function for nested functions), Global (module level), Built-in (Python's predefined names like print, len). Python searches these scopes in this order when resolving a name.
Question 8
Medium
When do you need the
global keyword and when do you need nonlocal?Think about which scope you are trying to modify.
Use
global to modify a module-level variable from inside a function. Use nonlocal to modify an enclosing function's variable from inside a nested function. Neither is needed for reading variables or for modifying mutable objects through their methods.Question 9
Hard
What is the output?
def make_greeting(greeting):
def greet(name):
return f"{greeting}, {name}!"
return greet
hello = make_greeting("Hello")
namaste = make_greeting("Namaste")
print(hello("Aarav"))
print(namaste("Priya"))
print(hello("Rohan"))Each closure captures a different greeting value.
Hello, Aarav!Namaste, Priya!Hello, Rohan!Question 10
Hard
What is the output?
print = "hello" # Shadows built-in print
try:
print("test")
except TypeError as e:
del print # Remove shadow
print(f"Error was: {e}")
print("Built-in restored")Assigning to 'print' shadows the built-in. del removes the shadow.
Error was: 'str' object is not callableBuilt-in restoredQuestion 11
Hard
What is the output?
def accumulator(start):
total = start
def add(n):
nonlocal total
total += n
return total
return add
acc = accumulator(100)
print(acc(10))
print(acc(20))
print(acc(30))The closure maintains a running total across calls.
110130160Multiple Choice Questions
MCQ 1
What does LEGB stand for?
Answer: B
B is correct. LEGB stands for Local, Enclosing, Global, Built-in. This is the order Python searches for variable names.
B is correct. LEGB stands for Local, Enclosing, Global, Built-in. This is the order Python searches for variable names.
MCQ 2
Where are local variables created?
Answer: B
B is correct. Local variables are created inside a function. They exist only during the function call and are destroyed when the function returns.
B is correct. Local variables are created inside a function. They exist only during the function call and are destroyed when the function returns.
MCQ 3
What keyword is used to modify a global variable from inside a function?
Answer: C
C is correct. The
C is correct. The
global keyword declares that a variable inside a function refers to the module-level global variable. nonlocal (A) is for enclosing scope, not global.MCQ 4
Can you read a global variable inside a function without any special keyword?
Answer: B
B is correct. You can read global variables from inside a function without the
B is correct. You can read global variables from inside a function without the
global keyword. You only need global when you want to assign to (modify) the global variable.MCQ 5
What is a closure?
Answer: C
C is correct. A closure is a function that captures and remembers variables from the enclosing scope, even after the enclosing function has returned.
C is correct. A closure is a function that captures and remembers variables from the enclosing scope, even after the enclosing function has returned.
MCQ 6
What error occurs if you assign to and read a variable in the same function without declaring it global?
Answer: C
C is correct.
C is correct.
UnboundLocalError occurs because Python sees the assignment, marks the variable as local for the entire function, and then encounters a read before the assignment.MCQ 7
What does the nonlocal keyword do?
Answer: C
C is correct.
C is correct.
nonlocal tells Python that a variable in a nested function refers to the enclosing (not global) scope's variable, allowing modification.MCQ 8
Which scope does Python search first when looking up a variable name?
Answer: D
D is correct. Python follows LEGB order: Local first, then Enclosing, then Global, then Built-in. It stops at the first scope where the name is found.
D is correct. Python follows LEGB order: Local first, then Enclosing, then Global, then Built-in. It stops at the first scope where the name is found.
MCQ 9
What happens when you do list = [1, 2, 3] at module level?
Answer: B
B is correct. A global variable named
B is correct. A global variable named
list shadows the built-in list function. The built-in still exists in the Built-in scope but is hidden. Use del list to remove the shadow.MCQ 10
Do you need the global keyword to append to a global list?
Answer: B
B is correct.
B is correct.
global is needed for reassignment (my_list = new_list). Calling methods on the existing object (my_list.append(1)) does not require global because the variable itself is not being reassigned.MCQ 11
What is the output of this code?
x = 10
def f():
x = x + 1
return x
f()Answer: C
C is correct.
C is correct.
x = x + 1 contains both a read and an assignment. Python marks x as local for the entire function. The read (x + 1) happens before the assignment, causing UnboundLocalError.MCQ 12
When Python determines variable scope, is it at compile time or runtime?
Answer: B
B is correct. Python determines scope at compile time (when the function definition is compiled). If an assignment to a variable exists anywhere in the function, it is treated as local for the entire function, regardless of execution flow.
B is correct. Python determines scope at compile time (when the function definition is compiled). If an assignment to a variable exists anywhere in the function, it is treated as local for the entire function, regardless of execution flow.
MCQ 13
Why do closures in a loop often capture the same value?
Answer: B
B is correct. Closures capture the variable, not its current value. In a loop, all closures reference the same loop variable. After the loop ends, the variable holds its final value, so all closures return that final value.
B is correct. Closures capture the variable, not its current value. In a loop, all closures reference the same loop variable. After the loop ends, the variable holds its final value, so all closures return that final value.
MCQ 14
How do you fix the loop-closure problem (all closures returning the same value)?
Answer: B
B is correct. A default parameter (
B is correct. A default parameter (
def f(i=i)) is evaluated at function definition time, capturing the current value of i. Each iteration creates a function with a different default, solving the shared-variable problem.MCQ 15
What does func.__closure__ contain?
Answer: C
C is correct.
C is correct.
__closure__ is a tuple of cell objects containing the captured variables from the enclosing scope. Access values with func.__closure__[0].cell_contents. If the function is not a closure, __closure__ is None.MCQ 16
Can nonlocal refer to a global variable?
Answer: B
B is correct.
B is correct.
nonlocal only searches enclosing function scopes. It cannot refer to global (module-level) variables. Use global for module-level variables. If no enclosing function has the variable, nonlocal raises a SyntaxError.MCQ 17
Which of the following is a built-in scope name?
Answer: C
C is correct.
C is correct.
len is a built-in function available in the Built-in scope. The other options are user-defined names that would be in Local or Global scope.MCQ 18
What is the output?
x = 5
def f():
global x
x = 10
def g():
nonlocal x # SyntaxError?
x = 20
g()
f()Answer: C
C is correct. In function
C is correct. In function
f(), x is declared global, so it is not a local variable of f. Function g() uses nonlocal x, but there is no enclosing function with a local x. This causes a SyntaxError.Coding Challenges
Challenge 1: Counter Using Closure
EasyWrite a function make_counter() that returns an increment function. Each call to the returned function should return the next integer starting from 1.
Sample Input
(No input required)
Sample Output
1
2
3
Use a closure with nonlocal. Do not use global variables.
def make_counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
counter = make_counter()
print(counter())
print(counter())
print(counter())Challenge 2: Multiplier Factory
EasyWrite a function make_multiplier(factor) that returns a function. The returned function should multiply its argument by factor. Create double (factor=2) and triple (factor=3) and test them.
Sample Input
(No input required)
Sample Output
double(5) = 10
triple(5) = 15
double(100) = 200
Use a closure. The returned function should take one argument.
def make_multiplier(factor):
def multiply(x):
return x * factor
return multiply
double = make_multiplier(2)
triple = make_multiplier(3)
print(f"double(5) = {double(5)}")
print(f"triple(5) = {triple(5)}")
print(f"double(100) = {double(100)}")Challenge 3: Logger Factory
EasyWrite a function make_logger(prefix) that returns a log function. The log function should print messages with the given prefix. Create 'ERROR' and 'INFO' loggers.
Sample Input
(No input required)
Sample Output
[ERROR] File not found
[INFO] Server started
[ERROR] Connection refused
Use a closure to remember the prefix.
def make_logger(prefix):
def log(message):
print(f"[{prefix}] {message}")
return log
error = make_logger("ERROR")
info = make_logger("INFO")
error("File not found")
info("Server started")
error("Connection refused")Challenge 4: Running Average Calculator
MediumWrite a function make_averager() that returns a function. Each call to the returned function with a number should return the running average of all numbers provided so far.
Sample Input
(No input required)
Sample Output
10.0
15.0
20.0
17.5
Use a closure to maintain state. Track sum and count using nonlocal.
def make_averager():
total = 0
count = 0
def add(value):
nonlocal total, count
total += value
count += 1
return total / count
return add
avg = make_averager()
print(avg(10))
print(avg(20))
print(avg(30))
print(avg(10))Challenge 5: Fix the Loop-Closure Bug
MediumThe following code creates functions that should return 0, 1, 2, 3, 4 but all return 4. Fix it using the default parameter technique.
funcs = []
for i in range(5):
def f():
return i
funcs.append(f)
print([fn() for fn in funcs]) # [4, 4, 4, 4, 4]Sample Input
(No input required)
Sample Output
[0, 1, 2, 3, 4]
Fix using default parameter: def f(i=i).
funcs = []
for i in range(5):
def f(i=i):
return i
funcs.append(f)
print([fn() for fn in funcs])Challenge 6: Scope Tracer
MediumWrite a program that demonstrates all four LEGB scopes. Define a global variable, a function with an enclosing variable, a nested function with a local variable, and use a built-in (like len). Print which scope each variable comes from.
Sample Input
(No input required)
Sample Output
Built-in: len = <built-in function len>
Global: x = global
Enclosing: x = enclosing
Local: x = local
Must demonstrate all four scopes: L, E, G, and B.
x = "global"
def outer():
x = "enclosing"
def inner():
x = "local"
print(f"Local: x = {x}")
print(f"Built-in: len = {len}")
print(f"Enclosing: x = {x}")
inner()
print(f"Global: x = {x}")
outer()Challenge 7: Rate Limiter Closure
HardWrite a function make_rate_limiter(max_calls) that returns a function. The returned function should track how many times it has been called. If called more than max_calls times, it should return 'Rate limit exceeded' instead of executing.
Sample Input
(No input required)
Sample Output
Call 1: OK
Call 2: OK
Call 3: OK
Call 4: Rate limit exceeded
Call 5: Rate limit exceeded
Use a closure with nonlocal to track the call count.
def make_rate_limiter(max_calls):
calls = 0
def check():
nonlocal calls
calls += 1
if calls > max_calls:
return "Rate limit exceeded"
return "OK"
return check
limiter = make_rate_limiter(3)
for i in range(1, 6):
print(f"Call {i}: {limiter()}")Challenge 8: Memoization Using Closure
HardWrite a function memoize(func) that returns a new function which caches results. If the same argument is passed again, return the cached result instead of recomputing. Test with a factorial function.
Sample Input
(No input required)
Sample Output
Computing factorial(5)...
120
120
Computing factorial(3)...
6
Use a closure with a dictionary as cache. Print 'Computing...' only on first call for each argument.
def memoize(func):
cache = {}
def wrapper(n):
if n not in cache:
print(f"Computing {func.__name__}({n})...")
cache[n] = func(n)
return cache[n]
return wrapper
def factorial(n):
if n <= 1:
return 1
return n * factorial(n - 1)
factorial = memoize(factorial)
print(factorial(5))
print(factorial(5))
print(factorial(3))Need to Review the Concepts?
Go back to the detailed notes for this chapter.
Read Chapter NotesWant to learn Python with a live mentor?
Explore our Python course