Codectionary / Developer documentation / Python

Variable scope and name lookup

Distinguish local bindings, enclosing function names and module-level state.

How it works

A name refers to an object within a scope. In ordinary function code, lookup searches local names, enclosing function scopes, the module namespace and built-ins. A local assignment normally creates a local binding rather than changing a module name.

Python determines local names for a function block from its bindings. Reading a local name before its value has been assigned raises UnboundLocalError, even when a module-level name with the same spelling exists.

Choices and common mistakes

Prefer explicit parameters and return values when passing information between functions. It makes data dependencies easier to follow.

A for or if block does not create a separate ordinary local scope. Comprehensions have their own scope in Python 3, so do not assume every indented construct behaves alike.

Try it yourself

Make a function return a locally formatted name while preserving the module-level original. Explain the distinction between rebinding a name and modifying a mutable object.

Syntax

label = "module"
def show():
    label = "local"
    return label

Examples

Keep a local label

Show that a function can use the same name independently.

label = "module"
def show():
    label = "local"
    return label

print(show())
print(label)
  1. The assignment inside show binds a local name. Returning it gives the caller the string local.
  2. The final print runs at module level, where label still refers to module. Returning a value does not rebind the caller’s names.
  3. Use local names for intermediate calculations that should not affect other operations.
  4. Expected output: local module

Read an enclosing value

Let a nested function use information from its creator.

def make_label(prefix):
    def label(name):
        return prefix + ": " + name
    return label

task_label = make_label("Task")
print(task_label("Read"))
  1. label can read prefix from the enclosing make_label call. Returning the nested function retains access to that binding.
  2. Calling task_label supplies name while prefix remains the captured value. No global variable is needed.
  3. This is useful for a small configured callback; see closures for lifetime and late-binding details.
  4. Expected output: Task: Read

Recognise a local-before-assignment error

Understand why a read does not fall back to a module variable.

count = 10
def broken():
    print(count)
    count = 1

try:
    broken()
except UnboundLocalError:
    print("Local count has no value yet")
  1. The assignment anywhere in broken makes count local throughout that function. The earlier print therefore reads an unassigned local.
  2. The handler reports the problem; it does not make the function correct. Passing count as an argument is often the clearer fix.
  3. Use this explanation when diagnosing an UnboundLocalError rather than adding global automatically.
  4. Expected output: Local count has no value yet

Best practices

  • Prefer explicit parameters and return values when passing information between functions. It makes data dependencies easier to follow.
  • A for or if block does not create a separate ordinary local scope. Comprehensions have their own scope in Python 3, so do not assume every indented construct behaves alike.

Runtime & versions

Python interpreter. Feature availability depends on the language and runtime version used by your project; consult the official reference below.

Python language reference

Specifications & further reading