Python · functions

Function Scope

Where a name lives, where Python looks for it, and how global and nonlocal change that.

Where Python looks: LEGB

Built-in
printlensum
Global (the module)
x = 10outer
Enclosing: outer()
y = 20inner
Local: inner()
z = 30

Inside inner(), a name is searched for from the inside out:

  1. Local
  2. Enclosing functions
  3. Global
  4. Built-in
Look up:

Global scope


          # global variable
          x = 10
          def func():
              print(x)

          func()
          # ans: 10
        

A function can read a global name: it isn't local, so the search moves outwards.

Local scope


          # local variable
          def func():
              y = 20
              print(y)

          func()
          # ans: 20
          print(y)
          # ans: NameError: name 'y' is not defined
        

A local variable lives in the call's frame and is gone once the call returns.

The global keyword


          # modify global variable
          count = 0
          def increment():
              global count
              count += 1

          increment()
          # ans: count is 1
        

global count makes assignments inside the function change the global frame's count.

The nonlocal keyword


          # modify enclosing scope
          def outer():
              x = 10
              def inner():
                  nonlocal x
                  x = 20
              inner()
              return x

          result = outer()
          # ans: 20
        

nonlocal reaches one level out, to the enclosing function's frame.

Shadowing


          # local shadows global
          x = 10
          def func():
              x = 5
              print(x)

          func()
          # ans: 5
          print(x)
          # ans: 10
        

Assigning inside a function creates a new local x; the global x is untouched.

Exercises

Predict the result, then press → to run the code and check.

Exercise 1


          # what is printed?
          x = 5
          def func():
              print(x)

          func()
          # ans: 5
        

Exercise 2


          # what happens?
          x = 5
          def func():
              x = 10

          func()
          print(x)
          # ans: 5 (the local x doesn't affect the global)
        

Exercise 3


          # global keyword?
          x = 5
          def func():
              global x
              x = 10

          func()
          print(x)
          # ans: 10
        

Exercise 4


          # can you do this?
          def func():
              print(x)
              x = 5

          func()
          # ans: UnboundLocalError
        

Python decides a name is local for the whole function if it is assigned anywhere in it.

Exercise 5


          # nonlocal?
          def outer():
              x = 5
              def inner():
                  nonlocal x
                  x = 10
              inner()
              return x

          outer()
          # ans: 10
        

Exercise 6


          # nested functions?
          def outer():
              x = 5
              def inner():
                  return x
              return inner()

          outer()
          # ans: 5
        

Exercise 7


          # what is x?
          x = 1
          def func():
              x = x + 1

          func()
          # ans: UnboundLocalError
        

The assignment makes x local, so x + 1 reads a local that has no value yet.

Exercise 8


          # parameter scope?
          def func(x):
              x = 10

          x = 5
          func(x)
          print(x)
          # ans: 5
        

Exercise 9


          # global in nested?
          x = 5
          def outer():
              def inner():
                  global x
                  x = 10
              inner()

          outer()
          print(x)
          # ans: 10
        

Exercise 10


          # closure?
          def outer(x):
              def inner(y):
                  return x + y
              return inner

          f = outer(10)
          f(5)
          # ans: 15
        

A closure: inner keeps x alive after outer has returned.

Remember

LEGB

Names are looked up Local, Enclosing, Global, then Built-in.

Assignment makes local

Assigning a name anywhere in a function makes it local there, unless declared otherwise.

global / nonlocal

Let a function change a module-level name, or one in the enclosing function.

Closures

An inner function remembers the enclosing variables it uses.