All Courses
Python Basics

Python Scope

1. What is Scope?

Scope defines where a variable is accessible in your program.

  • Variables defined inside a function are only accessible inside that function (local scope)
  • Variables defined outside all functions are accessible everywhere (global scope)
def foo():
    x = 0
    print(x)   # ✅ works — x is in scope here

print(x)       # ❌ NameError — x is not accessible outside foo

2. Local vs Global Scope

Global variables are accessible inside functions

x = 1

def foo():
    print(x)   # ✅ can access global x (no local x defined)

foo()          # → 1

Local variables shadow global ones

If a local variable has the same name as a global one, the local one takes priority inside the function.

x = 1

def foo():
    x = 0       # this is a NEW local variable, not the global x
    print(x)    # → 0

foo()
print(x)        # → 1  (global x unchanged)

Python first looks for a variable in the local (function) scope. If not found, it looks in the global scope.


3. Block Scope (if / for / while)

Unlike functions, variables defined inside if, for, or while blocks are accessible outside them — as long as they were created.

inp = int(input("Enter a number: "))

if inp > 5:
    value = "greater than five"
else:
    value = "not greater than five"

print(value)   # ✅ works — value was created in one of the branches

⚠️ Danger — variable may not exist

if inp > 5:
    value = "greater than five"

print(value)   # ❌ NameError if inp <= 5 — value was never created

Block variables are NOT accessible inside a function

value = "hello"     # global

def foo():
    print(value)    # ✅ can access global value

# BUT a variable created inside a function block is not accessible outside
def bar():
    if True:
        result = 10
# result is not accessible here

4. Scope with Function Parameters

Parameters are local to the function. Reassigning a parameter does not change the variable outside.

def add_five(x):
    x = x + 5     # only changes the local x
    print(x)      # → 15

x = 10
print(x)          # → 10
add_five(x)       # → 15
print(x)          # → 10  (unchanged)

5. Scope + Mutability

For mutable objects (lists, dicts, sets), changes made inside a function do affect the original — because the parameter points to the same object.

def append_five(x):
    x.append(5)

x = []
append_five(x)
print(x)   # → [5]  — original was modified!

Fix — copy the object to avoid modifying the original

def append_five(x):
    x = x[:]       # make a copy
    x.append(5)
    print(x)       # → [5]

x = []
append_five(x)
print(x)   # → []  — original untouched

This is the intersection of scope and mutability. Even though you can't reassign a global variable from inside a function, you can mutate a mutable object passed to it.


6. The global Keyword

Forces a variable inside a function to refer to the global variable instead of creating a new local one.

value = 5

def foo():
    global value
    value = 10     # modifies the GLOBAL value

foo()
print(value)       # → 10

⚠️ Avoid using global

Using global is considered bad practice because: - It creates hidden side effects — the function changes something outside itself - It makes code hard to debug and reason about - It's unpredictable when the same variable name is reused elsewhere

There is no situation in standard Python exercises that requires the global keyword. Avoid it.


7. Scope Summary

Location Accessible inside function? Accessible outside function?
Global variable ✅ Yes ✅ Yes
Local variable (in function) ✅ Yes ❌ No
Block variable (if/for) ✅ Yes (if created) ✅ Yes (if created)
Parameter ✅ Yes (local copy) ❌ No

Cheat Sheet

# Global accessible inside function
x = 10
def foo():
    print(x)    # ✅ works

# Local NOT accessible outside
def bar():
    y = 5
print(y)        # ❌ NameError

# Local shadows global (same name)
x = 10
def foo():
    x = 99      # local x, doesn't change global
    print(x)    # → 99
print(x)        # → 10

# Mutable object modified inside function
def modify(lst):
    lst.append(1)

a = []
modify(a)
print(a)        # → [1]

# Avoid mutation — copy first
def modify_safe(lst):
    lst = lst[:]
    lst.append(1)

# global keyword (avoid!)
val = 5
def foo():
    global val
    val = 10