Concepts / Variable Scope and Lifetime

Variable Scope and Lifetime

The type of a function object is function. Inspecting the function name reveals that Python created a function value, just as inspecting another variable reveals the kind of value it holds.

  • 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 Hold Instructions

A function definition does more than make a reusable piece of code available. It creates a variable, and that variable holds a function object. This means a function name is a name referring to a value, much as x can hold 5 or name can hold the string Alice.

print_lyricsvariablefunction objectinstructions in thefunction bodyrefers to
What does the name created by a function definition refer to?

What Definition Creates

When Python defines print_lyrics, the name print_lyrics becomes a variable that holds a function object. The object contains the instructions written inside the function definition. The name is therefore not merely a label for invisible code: it refers to a value that Python can inspect, print, and pass to another function.

Key point

The type of a function object is function. Inspecting the function name reveals that Python created a function value, just as inspecting another variable reveals the kind of value it holds.

What do you think happens?

After defining print_lyrics, what kind of value does the name print_lyrics hold?

  • A function object

  • A string containing the function's instructions

  • A number

Reveal answer
Answer:

A function object

A function definition creates a variable holding a function object, and that object's type is function.

Calling Changes the Execution Path

Defining a function creates the function object, but it does not mean that Python immediately runs every instruction in its body. Execution enters the body when the function is called. A custom function uses the same call syntax as a built-in function: the function name followed by parentheses.

callerprint_lyrics()first print statementsecond printstatementline after callreachesjumps intoruns in orderreturns to
What happens to control flow when a custom function is called?

The execution sequence is: Python reaches the call, pauses at that line, jumps into the function body, runs the statements in order, and then returns control to the line immediately after the call. The function call is therefore a temporary change in the execution path, not a replacement for the caller's remaining instructions.

Nested Calls and Returning Control

Worked example

Tracing repeat_lyrics

Trace the control flow when repeat_lyrics calls print_lyrics twice.

  1. 1
    Start with the outer call

    Execution reaches repeat_lyrics(), so control moves from the original caller into the body of repeat_lyrics.

  2. 2
    Enter the inner call

    The first call to print_lyrics moves control into the print_lyrics function body. Its statements run in order.

  3. 3
    Return to the outer function

    When the first print_lyrics call finishes, control returns to repeat_lyrics at the point after that inner call.

  4. 4
    Make the second inner call

    The second call to print_lyrics again enters its body and runs the same instructions.

  5. 5
    Return to the original caller

    After repeat_lyrics finishes, control returns to the line immediately after the original repeat_lyrics call.

Result

The flow is nested: original caller to repeat_lyrics, repeat_lyrics to print_lyrics, back to repeat_lyrics, into print_lyrics again, and finally back to the original caller.

This pattern is useful because a larger behavior can be assembled from smaller functions. repeat_lyrics does not need to duplicate the instructions inside print_lyrics; it reuses them by calling the function twice.

print_lyricsholds function objectprint_lyrics()body executesprint_lyrics()body executes againcalled with parenthesescan be called again
Where is the function name involved as execution moves from definition to repeated calls?

Scope and Lifetime Boundaries

For this topic, the important boundary is the distinction between the function name and a particular execution of the function body. Defining print_lyrics creates the variable holding the function object. Calling print_lyrics() transfers control into that object's instructions, and finishing the call transfers control back to the caller. The source establishes these creation and control-flow facts; it does not specify additional rules about when a function object is removed or about more advanced local and global name-scope rules.

Common Tracing Mistakes

  • Common mistake

    Treating a function name as only a label for hidden code.

    What it looks like

    Thinking of print_lyrics as separate from the value it refers to.

    Why it fails

    The function name is a variable holding a function object.

    Do this instead

    Think of print_lyrics as a name referring to a value whose type is function.

  • Common mistake

    Assuming that defining a function immediately runs its body.

    What it looks like

    Expecting the statements inside print_lyrics to execute merely because the function was defined.

    Why it fails

    The body runs when the function is called.

    Do this instead

    Separate the definition step from the call step.

  • Common mistake

    Forgetting the parentheses in a function call.

    What it looks like

    Using the function name when the goal is to execute the function.

    Why it fails

    The call syntax is the function name followed by parentheses.

    Do this instead

    Use print_lyrics() or the equivalent name-and-parentheses form when calling a function.

  • Common mistake

    Tracing only the inner function in a nested call.

    What it looks like

    Stopping the trace after print_lyrics finishes inside repeat_lyrics.

    Why it fails

    Control first returns to repeat_lyrics and later returns to the original caller.

    Do this instead

    Record every transfer: caller to outer function, outer function to inner function, back to outer function, then back to caller.

Execution Trace Practice

MEDIUM

Using the source example with repeat_lyrics and print_lyrics, write the order in which control visits these places: the original caller, repeat_lyrics, the first print_lyrics call, repeat_lyrics again, the second print_lyrics call, and the line after repeat_lyrics.

Hints
  • A function call moves control into the called function.
  • After the called function finishes, control returns to the point after that call.
  • The two calls to print_lyrics occur from inside repeat_lyrics.
Key point

A complete trace should show both levels of nesting: the original caller resumes only after repeat_lyrics has completed, and repeat_lyrics resumes between its two calls to print_lyrics.

Key Takeaways

  1. 1
    Defining a function creates a variable that holds a function object.
  2. 2
    The function object's type is function, and the object can be inspected like other values.
  3. 3
    A custom function is called with its name followed by parentheses, just like a built-in function.
  4. 4
    A call moves control into the function body and returns control to the line after the call when the body finishes.
  5. 5
    Functions can call other functions, creating nested execution that returns outward one level at a time.

At a glance

  • A function definition creates a variable holding a function object.
  • A function object has type function and can be inspected as a value.
  • Calling uses the function_name() form and begins execution of the function body.
  • Control returns to the caller after the function finishes.
  • Nested calls return control outward through each function that was waiting.

More concepts starting with V