0%

34- Scope & Variable Lifetime

Where do variables live? How long do they last? Local vs global. Nested scopes. The rules that govern every variable in your program.

You create variables everywhere. Inside functions. Outside functions. Inside loops. Inside conditionals. But not all variables are created equal. Some are visible everywhere. Some are hidden inside functions. Some disappear when the function ends. Some live on. Scope is the region of a program where a variable is accessible. Lifetime is how long the variable exists in memory. Understanding scope prevents bugs. It helps you avoid accidentally modifying variables you did not mean to touch. It lets you write functions that are self-contained and predictable. This lesson covers the rules of scope in Python: local, global, enclosing (nonlocal), and built-in scopes. Master these, and you master variable visibility.

🕯️ Magic Note

Python uses the LEGB rule to resolve variable names: Local, Enclosing, Global, Built-in. When you use a variable name, Python searches in this order. First the local function scope. Then any enclosing functions (functions inside functions). Then the global module scope. Finally the built-in scope (like print and len). This explains why you can use len() without defining it.

Local Scope
Variables defined inside a function are local. They exist only inside that function. You cannot access them from outside.

Python

def my_function():

x = 10 # Local variable

print(f”Inside function: {x}”)

my_function() # Inside function: 10

# print(x) # NameError: name ‘x’ is not defined

💡 Local variables are created when the function is called and destroyed when the function returns. This is why they are not accessible outside. Each function call gets its own fresh copy of local variables.
Global Scope
Variables defined outside any function are global. They can be accessed from anywhere in the module, including inside functions (for reading).

Python

message = “Hello, world!” # Global variable

def show_message():

print(message) # Can READ global variable

show_message() # Hello, world!

⚠️ You can read global variables inside functions. But assignment is different. If you assign to a variable inside a function, Python creates a local variable unless you declare it global.

Python

count = 0 # Global variable

def increment():

count = count + 1 # ERROR! count on right is local, but uninitialized

print(count)

# increment() # UnboundLocalError: local variable ‘count’ referenced before assignment

# Why? Python sees the assignment and treats ‘count’ as local.

# But then you try to read it before assigning a value.

The global Keyword
To modify a global variable inside a function, use the global keyword.

Python

counter = 0 # Global variable

def increment():

global counter # Declare that we are using the global counter

counter += 1

print(f”Counter: {counter}”)

increment() # Counter: 1

increment() # Counter: 2

increment() # Counter: 3

print(f”Outside: {counter}”) # Outside: 3

🕯️ Magic Note

Using global is often discouraged. Functions that modify global variables are harder to test and debug. They create hidden dependencies. A better approach is to pass values as parameters and return results. Use global sparingly, for truly global state like configuration or counters that must persist across many functions.

Enclosing Scope (Nested Functions)
When you have a function inside another function, the inner function can access variables from the outer function. This is called enclosing scope.

Python

def outer():

x = 10 # Enclosing variable

def inner():

print(f”Inner sees x: {x}”) # Can read enclosing variable

inner()

outer() # Inner sees x: 10

The nonlocal Keyword
To modify an enclosing (non-global) variable from an inner function, use the nonlocal keyword.

Python

def outer():

x = 10 # Enclosing variable

def inner():

nonlocal x # Declare that we want to modify the enclosing x

x += 5

print(f”Inner: {x}”)

inner()

print(f”Outer after inner: {x}”)

outer()

# Inner: 15

# Outer after inner: 15

🕯️ Magic Note

nonlocal is like global but for enclosing function scopes. It is used in closures and decorators. Without nonlocal, you can read enclosing variables but not assign to them. Python would create a new local variable instead.

Built-in Scope
Python has a built-in scope containing functions like print(), len(), range(), and type(). You can use them anywhere without defining them.

Python

# Built-in functions are always available

print(len(“hello”)) # 5

print(type(42)) # <class ‘int’>

# But you can shadow them (not recommended)

len = 100 # Now len is an integer, not the function!

# print(len(“hello”)) # TypeError: ‘int’ object is not callable

# To restore shadowed built-ins, you can delete the variable

del len

print(len(“hello”)) # 5 (back to normal)

⚠️ Never use built-in function names as variable names. This is called shadowing. It causes confusing bugs and makes your code harder to understand. Avoid: len, list, dict, str, int, print, input, etc.
The LEGB Rule in Action
When Python sees a variable name, it searches in this order: Local, Enclosing, Global, Built-in. The first match wins.

Python

x = “global” # Global

def outer():

x = “enclosing” # Enclosing

def inner():

x = “local” # Local

print(f”Inner sees: {x}”)

inner()

print(f”Outer sees: {x}”)

outer()

print(f”Global sees: {x}”)

# Inner sees: local

# Outer sees: enclosing

# Global sees: global

Variable Lifetime
A variable’s lifetime is how long it exists in memory. Different scopes have different lifetimes.
ScopeLifetime
LocalCreated when function is called, destroyed when function returns
EnclosingLives as long as the outer function is executing
GlobalLives from module load until program ends
Built-inLives for entire program execution

Python

def create_counter():

count = 0 # Local to create_counter

def counter():

nonlocal count

count += 1

return count

return counter # Returns a closure

my_counter = create_counter()

print(my_counter()) # 1

print(my_counter()) # 2

print(my_counter()) # 3

# The count variable lives on because the inner function references it

# This is a closure. The variable is enclosed even after outer function ends.

🕯️ Magic Note

Closures extend the lifetime of variables. Normally, local variables are destroyed when a function ends. But if an inner function references them, they are kept alive. This is how decorators and closures work.

Scope and Loops
In Python, loops and conditionals (if, for, while) do NOT create new scopes. Variables defined inside them are in the same scope as the surrounding function or module.

Python

# Loop variable is accessible outside the loop (unlike some languages)

for i in range(5):

pass

print(i) # 4 (last value of i)

# Variable defined inside if is accessible outside

if True:

x = 100

print(x) # 100

# But this can be dangerous

if False:

y = 200

# print(y) # Still NameError! y was never assigned because condition was False

⚠️ Unlike C, Java, or JavaScript, Python does not have block-level scope. Only functions create new scopes. This means loop variables “leak” out of the loop. Be aware of this behavior. It is intentional but can surprise programmers from other languages.
Avoiding Global Variables
Global variables are convenient but dangerous. They create hidden dependencies and make code hard to test. Here are better alternatives.

Python

# Bad: Using global variable

user_name = “”

def set_name(name):

global user_name

user_name = name

def greet():

print(f”Hello, {user_name}!”)

# Better: Pass parameters and return values

def set_name(name):

return name

def greet(user_name):

print(f”Hello, {user_name}!”)

name = set_name(“Ali”)

greet(name)

# Even better: Use a class (covered in OOP section)

Common Scope Mistakes
  • Trying to modify a global variable without global
  • Trying to modify an enclosing variable without nonlocal
  • Shadowing built-in names
  • Assuming blocks (if, for, while) have their own scope
  • Using global excessively when parameters would work better
  • Creating circular references with closures that cause memory leaks (advanced)
Check Your Understanding
  • What is the difference between local and global scope?
  • When do you need to use the global keyword?
  • What does LEGB stand for?
  • How is nonlocal different from global?
  • Can you access a loop variable outside the loop?
  • Why is relying on global variables considered bad practice?

⚡ Whisper

Every variable is born somewhere. Some are born in the light of the global scope, visible to all. Others are born in the shadows of a function, hidden and private. Some live for a moment, then vanish when the function ends. Others live forever, clinging to memory in a closure. Know the birthplace of your variables. Know their lifetime. A variable that lives too long becomes a ghost, haunting your code with unexpected values. A variable that dies too soon leaves behind confusion. And a variable that is born in the wrong scope becomes a stranger, unknown to the parts that need it. Scope is not a technicality. It is the geography of your code. It tells you who can see what. It protects you from accidental changes. Learn to read this geography. Place your variables where they belong. Let them live as long as needed, no longer. Then your code will be clear, predictable, and safe.

Related posts