VMware ESXi and vSphere Cluster Management

Python Variable Scope: Local and Global Variables

Learn Python variable scope, local and global variables, LEGB lookup, shadowing, global and nonlocal, and safer function design.

Variable scope is the region of a program where a name can be looked up and used. Scope determines whether Python can access a name at a particular location.

In Python, a variable is a commonly used term for a name bound to an object or value. A name and its value are different things: the name is the label, while the value is the object currently associated with that label. For example, after color = "blue", color is the name and "blue" is its current value.

A name binding is the association created when Python assigns an object to a name. Where that assignment occurs affects where the name can later be referenced.

Module-level variables

A name assigned outside every function and class is defined at module scope. It is often called a global variable because functions in that module can look outward and read it.

pet = "otter"

def show_first():
    print(pet)

def show_second():
    print(pet)

show_first()
show_second()

Both functions print otter. Neither function has a local name called pet, so Python finds the module-level name.

The assignment must execute before code tries to use the name. For example, calling a function before the module-level assignment has run can produce a NameError.

Function-local variables

A name assigned inside a function is normally local to that function. It exists for that function's execution and cannot normally be accessed directly by module-level code or by another function.

def create_label():
    label = "draft"
    print(label)

def show_label():
    print(label)

create_label()
show_label()

The first call prints draft. The second call raises NameError because label was created inside create_label, not in show_label or module scope.

Each function has its own local namespace. A local name in one function is not automatically shared with another function.

Name shadowing

Shadowing occurs when an inner scope uses the same name as an outer scope. The inner binding is found first, so it temporarily hides the outer binding at that location.

status = "open"

def preview():
    status = "closed"
    print(status)

def report():
    print(status)

preview()
report()

preview prints closed, while report prints open. The assignment inside preview creates a local binding by default. It does not change the module-level binding.

Python's LEGB name lookup order

When Python evaluates a name, it searches using the LEGB rule:

Lookup levelWhere the name is definedExample
LocalInside the current functionamount assigned in the current function
EnclosingInside an outer function surrounding a nested functiontotal in make_counter
GlobalAt module levelsettings = {} outside functions
Built-inProvided by Pythonprint, len, and max

Python searches Local, then Enclosing, then Global, then Built-in. It uses an outer scope only when the name is not found in an inner scope. The basic examples in this lesson mainly use local and global lookup.

The enclosing level applies to nested functions. The built-in level supplies names such as print and len when your code has not defined a closer name with the same spelling.

Reading versus rebinding a global name

A function may read a global name without special syntax:

tax_rate = 0.2

def show_tax_rate():
    print(tax_rate)

show_tax_rate()

Reading asks Python for the current value. Assignment is different: an assignment inside a function creates or updates a local binding by default.

To rebind a module-level name from inside a function, use the global statement:

score = 0

def add_point():
    global score
    score = score + 1

add_point()
print(score)

The final output is 1. The declaration tells Python that assignments to score in add_point target the module-level name.

Name assigned where?Can current function read it?Can another function directly read it?Can assignment in a function change it without a declaration?
Module levelYes, if no local name shadows itYes, if the name is in that module's accessible global scopeNo; assignment creates a local name
Inside one functionYes, during that function's executionNo, not directlyOnly that function's local binding is changed
Inside an enclosing functionYes, for the enclosing function and nested functions that can see itOnly a nested function can access it through enclosing scopeUse nonlocal in the nested function to rebind it

Why UnboundLocalError occurs

UnboundLocalError is a subclass of NameError. It commonly occurs when a function reads a name before assigning to that same name:

count = 10

def show_count():
    print(count)
    count = count + 1

show_count()

Because the function contains an assignment to count, Python treats count as local throughout that function. The first print therefore tries to read a local name before it has a value.

Fix this by using a separate local name, passing the value in and returning an updated value, or deliberately declaring global when changing module state is truly intended.

Nested functions and nonlocal

A nested function is a function defined inside another function. The nested function can read names from its enclosing scope.

def make_counter():
    total = 0

    def increment():
        return total + 1

    return increment

To assign to an enclosing function's name from the nested function, use nonlocal. Without it, an assignment would create a new local binding inside the nested function.

def make_counter():
    total = 0

    def increment():
        nonlocal total
        total += 1
        return total

    return increment

counter = make_counter()
print(counter())
print(counter())

The calls return 1 and then 2. nonlocal targets a name in an enclosing function. It does not target module scope; that is the role of global.

DeclarationTargets which scope?Valid locationTypical use
globalModule-level scopeInside a functionRebind a module-level name from a function
nonlocalAn enclosing function's scopeInside a nested functionRebind state held by an outer function

Prefer parameters and return values

Most functions are easier to understand when their inputs and outputs are explicit:

def add_point(current_score):
    return current_score + 1

score = 0
score = add_point(score)
print(score)

This version produces 1 without modifying a global variable from inside the function. The caller supplies the input and receives the result.

  • Use function parameters for inputs.
  • Use return values for outputs.
  • Keep global mutable state to a minimum.
  • Use descriptive names to reduce accidental shadowing.
  • Avoid names such as list, str, and max, which shadow built-in names.

Common scope problems

  • NameError: name 'item' is not defined: The name may have been created inside another function, misspelled, or referenced before assignment. Pass it as a parameter, return it, define it in an appropriate outer scope, or correct the spelling.
  • UnboundLocalError: local variable referenced before assignment: The function both reads and assigns the same name. Use a separate local name, parameters and return values, or an intentional global or nonlocal declaration.
  • A function prints a different value from code afterward: A local assignment may be shadowing a global or enclosing name. Rename the local variable or explicitly choose the intended scope.
  • A function unexpectedly changes program-wide state: It may modify a mutable global object or rebind a global name. Prefer explicit inputs and outputs, and limit shared state.
  • A built-in such as len() stops working: A variable may be named len, list, str, or another built-in. Rename it and restart the interpreter or remove the accidental binding.

Running the examples

Save an example in a file named variable_scopes.py, then run it from a terminal:

python variable_scopes.py

To check which Python interpreter version is being used:

python --version

Exam-relevant summary

  • Scope is the part of code where a name can be found.
  • A name assigned at module level is global to that module.
  • A name assigned inside a function is local by default.
  • Python searches names using Local, Enclosing, Global, Built-in order.
  • A function can read a global name without global.
  • Rebinding a global name inside a function requires global.
  • A nested function reads an enclosing name through enclosing scope and needs nonlocal to rebind it.
  • Inner bindings shadow outer bindings; equal names do not imply the same binding.
  • Parameters and return values are generally clearer than global state.