Skip to main content

Scope and Nested Functions

Why Scope Matters — Preventing Bugs and Clarifying Intent

The Problem: You define a variable inside a function, then try to use it outside. Python says "NameError: variable not defined." You modify a global variable inside a function expecting the change to persist, but it doesn't.

The Reality: Variables have scope—regions of code where they exist and are accessible. Understanding scope prevents bugs and clarifies your intent.

💻 Code Idea: Scope Boundaries

Loading Python environment...

Key Insight: Variables defined inside a function are local. Variables defined at module level are global. Each function has its own local scope.

💬 AI Colearning Prompt

Ask your AI: "Explain what's wrong with this code and why. Then ask: How would you fix it?" (Provide code with scope error like modifying global without declaration, or trying to access local variable outside function)

Expected Understanding: You see that scope errors are real and understanding scope prevents them.

🎓 Instructor Commentary

Scope is about clarity and preventing bugs. When you see a variable, understanding WHERE it exists helps you predict WHAT the code does. This is how you validate code before running it—a critical AI-native developer skill. Good code minimizes dependencies on global state. When you're tempted to use global, that's often a signal to reconsider your design. Ask yourself: "Should this really be global? Could I pass it as a parameter instead?" Often the answer is yes.


Local Scope — Variables Inside Functions

Pattern: Variables defined inside a function exist only within that function. Once the function returns, those variables cease to exist.

💻 Code Idea: Local Scope Isolation

Loading Python environment...

Key Observation: Functions are isolated units. Variables inside one function don't affect another function with the same variable name. This isolation is a feature, not a bug.


Global Scope — Module-Level Variables

Pattern: Variables defined at module level (outside any function) are in global scope. Any function can READ them, but MODIFYing requires the global keyword.

💻 Code Idea: Reading vs. Modifying Global Variables

Loading Python environment...

Critical Distinction:

  • READING a global variable: Just use it, no global keyword needed
  • MODIFYING a global variable: Use global variable_name first

💻 Code Idea: Variable Shadowing (Accidental Mistake)

Loading Python environment...

Shadowing Lesson: When you assign to a variable inside a function, Python creates a LOCAL variable with that name. It shadows (hides) the global variable. To modify the global, you must use the global keyword first.


The global Keyword — Use Sparingly

When to Use: Rarely. When you absolutely must modify module-level state, use global.

Better Approach: Design your functions to take parameters and return values. Avoid global state.

💻 Code Idea: Design Without Global State

Loading Python environment...

Why better? The function's behavior is clear from its signature. Input and output are explicit. No hidden dependencies on global state.


Enclosing Scope and Nested Functions

Pattern: A function defined inside another function is a nested function. The inner function can access variables from the outer function's scope.

💻 Code Idea: Nested Functions and Closures

Loading Python environment...

Key Concept - Closure: The inner function "closes over" the outer function's variables. Even after outer_function returns, the returned double function remembers the value of multiplier (2). This is called a closure.

💻 Code Idea: Practical Closure Example

Loading Python environment...

Why this matters: Closures enable partial application and function factories. The outer function creates specialized versions of the inner function. This is a powerful pattern used throughout Python.


LEGB Rule — Python's Scope Order

LEGB stands for: Local, Enclosing, Global, Built-in

Python searches for variables in this order:

  1. Local — Inside current function
  2. Enclosing — In outer function (for nested functions)
  3. Global — At module level
  4. Built-in — Python's built-ins (print, len, etc.)

💻 Code Idea: LEGB Rule Demonstration

Loading Python environment...

How it works: Each x is in a different scope. When Python looks up x, it starts in Local, then Enclosing, then Global. It stops at the first match.


Scope as Design Tool — Avoiding Mistakes

💻 Code Idea: Scope-Aware Function Design

Loading Python environment...

Note: The nonlocal keyword (for completeness) modifies enclosing scope variables. Like global, use it sparingly.


🚀 Specification Challenge

Write a nested function that creates a personalized greeting generator:

  1. The outer function takes a greeting phrase (e.g., "Hello")
  2. The inner function takes a name and returns a full greeting
  3. Test that multiple returned functions remember their own greetings

Then ask your AI: "Is this a closure? How? Why is this useful?"

This teaches you how nested functions and closures enable elegant designs.


✨ AI Tool Tip

When you see the global keyword in code, pause and think: "Is this really necessary?" Good Python code rarely uses global. If you're tempted to use global, ask your AI: "Is there a better design pattern?" Often there is—like returning values, using classes, or using closures.


Try With AI

Understand variable scope through prediction, debugging, and closure exploration.

🔍 Explore LEGB Scope Resolution:

"Create nested functions with variable 'counter' at global, enclosing, and local scopes. Explain Python's LEGB lookup order (Local, Enclosing, Global, Built-in) and demonstrate which 'counter' gets accessed at each level."

🎯 Practice Scope Prediction:

"Analyze this code: counter = 0 (global), def outer(): counter = 10, def inner(): counter = 20. Predict output without running. Then show three versions: using global, using nonlocal, and redesigned with parameters/returns."

🧪 Test Closure Behavior:

"Debug this closure bug: def create_multipliers() with for i in range(3): multipliers.append(lambda x: x * i). Why do all functions return the same value? Show three fixes: default argument, closure factory, list comprehension."

🚀 Apply Scope Debugging:

"Build a scope debugging toolkit: inspect_scope() showing locals() vs globals(), and debug_closure(func) displaying captured variables via closure. Apply it to a buggy score tracker where assignment shadows global instead of modifying it."