
Variable scope in Python
Suppose a program has two functions, and both use a variable named result inside:
Python 3.13def area_circle(radius): result = 3.14 * radius * radius return result def area_square(side): result = side * side return result
These two result variables don't interfere with each other: each function works with its own name, and one doesn't affect the other. This is called variable scope: the rule Python uses to decide which variable a name refers to at any given point in the code.
What is variable scope?
Scope is the part of the program where a variable is available. It determines where a name can be used and which object it refers to.
Python has two main scope levels:
- global scope: anything declared at the top level of a module, visible inside every function
- local scope: variables declared inside a function, alive only while that function is running
A useful image: scopes are like rooms in an apartment. Things on your personal desk are accessible only to you (a local variable inside a function). A note on the fridge is visible to the whole family (a global variable).

Global variables
Global variables are defined outside any function and accessible both from within functions and outside them:
Python 3.13# Create a global variable message = "Hello, world!" def show_message(): # Use the global variable inside the function print(message) show_message()Hello, world!print(f"Variable outside the function: {message}")Variable outside the function: Hello, world!
The more globals you use, the harder the code is to debug: to understand what a function does, you have to look beyond its arguments and also remember which outside variables it reads.
Local variables
Local variables are created inside a function and exist only while that function is running. Once it returns, they disappear from memory:
Python 3.13def calculate_sum(): a = 10 b = 20 result = a + b print(f"Sum inside the function: {result}") calculate_sum()Sum inside the function: 30# Trying to access a local variable outside the function try: print(f"result: {result}") except NameError as e: print(f"Error: {e}")Error: name 'result' is not defined
The variable result exists only inside calculate_sum() and doesn't leak out.
When names collide
If a function creates a variable with the same name as a global one, the local one takes precedence. This is called shadowing: the local "shadows" the global without touching it:
Python 3.13x = "global" def test_scope(): x = "local" print(f"Inside function: x = {x}") test_scope()Inside function: x = localprint(f"Outside function: x = {x}")Outside function: x = global
These are two completely different variables that happen to share a name. Like two people with the same name living in different cities: same name, different individuals.
Modifying a global variable from a function
By default, any assignment inside a function is treated as creating a new local variable:
Python 3.13x = "global" def modify_global(): x = "new local" # This is a local, the global is unchanged print(f"Inside function: x = {x}") modify_global()Inside function: x = new localprint(f"Outside function: x = {x}")Outside function: x = global
To actually have a function change a global variable, you must explicitly say global x at the start of the function:
Python 3.13x = "global" def modify_global(): global x x = "global changed" print(f"Inside function: x = {x}") modify_global()Inside function: x = global changedprint(f"Outside function: x = {x}")Outside function: x = global changed
The global keyword signals to anyone reading the code: "this function isn't just doing its own job, it reaches outside." For that reason, use it only when no other path is available.
The most common trap: UnboundLocalError
A function reads a global variable and immediately reassigns it:
Python 3.13x = 10 def show(): print(x) # read x = x + 1 # and reassign right after
You'd expect the function to print 10 and bump x up to 11. Instead, running it crashes with UnboundLocalError: local variable 'x' referenced before assignment.
What's going on? Python looks at the function as a whole before executing it and decides which names are local. It sees x = ... inside, so x is declared in this function and is therefore local. On the top line, print(x) then tries to read the local x, which doesn't yet have a value, and crashes.
For this function to work with the global x, you'd need global x at the top. But usually there's a cleaner path: don't touch the global, take x as a parameter and return the new value:
Python 3.13def increment(value): return value + 1 x = 10 x = increment(x) print(x)11
When global is justified, and when it isn't
The most predictable way to handle data is: don't modify globals; pass values through parameters and return new ones via return:
Python 3.13def add(a, b): return a + b x = 5 y = 10 sum_result = add(x, y) print(f"{x} + {y} = {sum_result}")5 + 10 = 15
This kind of function is honest: its result is fully determined by what came in. Nothing happens behind the scenes. That makes both testing and reading easier: a glance at the signature tells you everything.
global is justified when a variable really should live at program level: a configuration value, a shared counter. But in most practice tasks it isn't needed.
Understanding check
Let's see how well you've understood variable scope.
In the next lesson we'll look at basic data types in Python: what's behind int, str, float, bool, and which operations are defined on each.
