Concepts / Variable Scope and Namespace

Variable Scope and Namespace

The same spelling can appear in the outermost namespace and inside a function, but assignment inside the function is local by default.

  • Programming
Interactive lab

Try it: Scope: Local, Enclosing, Global, Built-in

How Python looks up a name Local → Enclosing → Global → Built-in, why assigning to a name inside a function makes it local (and raises UnboundLocalError if it is read first), and what global, nonlocal and closures change.

How it works

  1. Before a function runs, Python marks every name assigned inside it as local to that function, unless it is declared global or nonlocal.
  2. Reading a name searches the local namespace, then enclosing functions, then the module's globals, then the built-ins; the first hit wins.
  3. A local name read before it has a value raises UnboundLocalError (Python does not fall back to the global).
  4. global x makes assignments go to the module namespace; nonlocal x makes them go to the nearest enclosing function.
  5. A function's local frame disappears when it returns, unless an inner function (a closure) still uses its variables.

Default run (10 steps): Ready: Local shadows global. Every name is looked up Local, then Enclosing, then Global, then Built-in. … Finished. Printed: inside: 99 / outside: 10.

Simplified: The lab's own small Python interpreter runs a fixed, hand-written script (you choose the variant and the values, never the code); nothing is executed as real Python. Every script was run in CPython 3.12 with the same inputs and the lab reproduces its output and error messages. Built-ins shows only the names the script used.

Educational simulation

Loading the simulation…

A Name Can Belong to Different Scopes

A variable name does not automatically refer to one universally shared place. Python organizes names through namespaces, and a function has its own local namespace while the program also has an outermost namespace. The important rule for this lesson is that Python localizes a variable by default when a function assigns to that name. This is why a function cannot modify a top-level variable through assignment unless it explicitly uses the global statement.

Outermost namespacecounterFunction namespacecounterAssignmentlocal by defaultassigns to countercreates local binding
Which namespace receives a name when a function assigns to it?
Key point

The same spelling can appear in the outermost namespace and inside a function, but assignment inside the function is local by default.

Trace an Assignment Without global

What do you think happens?

Suppose the outermost namespace contains score with the value 10. A function assigns score = 20 without a global statement. Which binding does that assignment change?

  • The outermost score binding

  • A local score binding inside the function

  • Both bindings

  • Neither binding

Reveal answer
Answer:

A local score binding inside the function

Python localizes names by default when a function assigns to them. The assignment does not modify the outermost variable.

Code walkthrough
score = 10

def set_score():
    score = 20

set_score()
  1. 1
    Before the call, the outermost score binding has the value 10.
  2. 2
    During the call, the assignment creates or updates a local score binding with the value 20.
  3. 3
    After the call, the local binding is no longer the binding being used, and the outermost score remains 10.
  • The outermost namespace binds score to 10.
  • The function assigns to a name called score.
  • Because there is no global statement, this assignment is local to the function.
  • The function call completes without changing the outermost score.
score10score20set_scoreassignment creates local bindingouter value remains available
What changes when a function assigns to a same-named variable instead of only reading the outermost value?

Reading Versus Writing

Reading a global variable from inside a function works without global. Writing to that global variable requires the global statement. This distinction matters when a function both reads and writes a name: without global, Python treats the assigned name as local, so the function cannot use that same name as an outer-scope value in the intended way. The source identifies this mixed read-and-write situation as causing UnboundLocalError.

python
Output
The function can read the outermost limit without declaring global.
python
Output
The global statement makes the function's count name refer to the outermost count binding, so the assignment can modify it.
show_limitRead limitincrease_countglobal countAssign countreads without globaldeclares shared bindingpermits write
What is the difference between a function that reads a global value and one that changes shared global state?

Declare the Shared Binding

The global statement creates an explicit binding between a local name and a global name. Place the statement inside the function before the assignment that must change the outermost variable. After that declaration, the function has both read and write access to the outer-scope variable.

Code walkthrough
visits = 0

def record_visit():
    global visits
    visits = visits + 1

record_visit()
record_visit()
  1. 1
    Initially, the outermost visits binding has the value 0.
  2. 2
    The first call reads 0 and assigns 1 to the global binding.
  3. 3
    The second call reads 1 and assigns 2 to the same global binding.
  4. 4
    The function can perform these writes because it uses global visits.
  • visits is defined in the program's outermost namespace.
  • record_visit declares that its visits name is bound to the global name.
  • The assignment reads the current global value and writes back an increased value.
  • Each call changes the shared outermost value.
Function callglobal visitsRead visitsAssign visitsUpdated outermostvalueenter functionuse global bindingcompute new valuewrite shared state
How does control flow and variable lookup change when a function declares a name with global before assigning to it?
Best practice

Use global only when a function genuinely needs to modify a variable defined in the program's outermost namespace. Keeping the declaration close to the function's assignment makes the shared state visible to readers of the code.

Observe the State Change

Before
status = "waiting"

def start_task():
    global status
    status = "running"

Before the function call, the outermost status value is waiting.

After
status = "waiting"

def start_task():
    global status
    status = "running"

start_task()

After the call, the outermost status value is running.

statuswaitingstart_taskglobal statusstatusrunningfunction callassignment completes
How does a global variable's value change before, during, and after a function call that uses global?

This example shows the defining effect of global: the function is not merely creating a temporary local name. It is changing shared state in the outermost namespace, and later code that uses that outermost name can observe the new value.

Clarity and Safer Data Flow

A global variable can be convenient, but global state reduces code clarity and testability. A function that changes shared state has an effect that is not visible from its parameters or return value. This makes it harder to see where data comes from and where it changes. The recommended default is to pass data through parameters and return values, using global only when necessary.

ApproachData movementClarity concern
Read a global valueThe function obtains a value from outer-scope stateThe dependency is outside the parameter list
Write a global valueThe function changes shared outer-scope stateThe side effect may be harder to track and test
Use parameters and return valuesThe caller supplies data and receives a resultThe data flow is more explicit

Global state can be used, but explicit inputs and outputs generally make a function's data flow clearer.

python
python

The first version changes shared state through global. The second version makes the input and result explicit with a parameter and a return value. When the second design fits the task, it is usually easier to understand and test because the function's data flow is visible at the call site.

Mistakes with Global Names

  • Common mistake

    Expecting assignment inside a function to update the outermost variable automatically.

    What it looks like
    score = 10
    
    def change_score():
        score = 20
    Why it fails

    Python localizes variables by default on assignment, so the assignment creates or updates a local binding.

    Do this instead

    Use global score before the assignment if modifying the outermost score is genuinely required.

  • Common mistake

    Reading and writing a global name without declaring it global.

    What it looks like
    count = 0
    
    def increase():
        count = count + 1
    Why it fails

    The source identifies mixing reads and writes without global as causing UnboundLocalError.

    Do this instead

    Declare global count before the assignment, or redesign the function to receive count as a parameter and return the new value.

  • Common mistake

    Using global for every value shared with a function.

    What it looks like

    A function changes shared state even though the caller could have passed the value as an argument.

    Why it fails

    Global state reduces code clarity and testability.

    Do this instead

    Prefer passing data through parameters and return values when that communicates the data flow clearly.

Practice the Binding Decision

MEDIUM

For each situation, decide whether the function needs global. Then explain why.

  1. A function only reads an outermost variable called limit.
  2. A function assigns a new value to an outermost variable called mode.
  3. A function receives a value as a parameter and returns an updated value without changing any outermost variable.
  4. A function both reads and assigns count using the same name, but has no global statement.
Hints
  • Reading a global variable does not require global.
  • Writing to a global variable requires global.
  • Parameters and return values make data flow explicit.
  • The mixed read-and-write case without global causes UnboundLocalError.
Worked example

Choosing a binding strategy

A program has an outermost variable called attempts. A function should increase it each time it is called. What must the function do, and what alternative design could make the data flow clearer?

  1. 1
    Identify the operation

    The function must write a new value to a variable defined in the outermost namespace.

  2. 2
    Apply the binding rule

    Assignment inside a function is local by default, so the function needs global attempts before assigning to the outermost attempts.

  3. 3
    Consider the design

    Because global state can reduce clarity and testability, another design is to pass attempts as a parameter and return the increased value.

Result

Use global attempts when modifying the shared outermost variable is necessary; otherwise prefer an explicit parameter-and-return-value design.

Key Takeaways

  1. 1
    Python localizes a name by default when a function assigns to it.
  2. 2
    Reading a global variable works without global, but writing to it requires global.
  3. 3
    The global statement explicitly binds the function's name to the outermost name, allowing reads and writes.
  4. 4
    Mixing reads and writes without global causes UnboundLocalError.
  5. 5
    Global state can reduce clarity and testability, so prefer parameters and return values when they make data flow explicit.

At a glance

  • Assignment inside a function is local by default.
  • Use global before assigning to a variable defined in the program's outermost namespace.
  • Reading a global does not require global, but mixing a read and write without it causes UnboundLocalError.
  • Use global sparingly because shared global state can reduce clarity and testability.
  • Prefer parameters and return values when they provide a clearer data flow.

Related concepts

More concepts starting with V