How to Solve "UnboundLocalError: local variable referenced before assignment" in Python
The UnboundLocalError: local variable 'variable_name' referenced before assignment
error in Python occurs when you try to use a variable within a function before it has been assigned a value within that function's scope, even if a variable with the same name exists in an outer (e.g., global) scope.
This guide explains why this error happens, and presents several solutions, including the global
and nonlocal
keywords, and best-practice alternatives that often make your code cleaner and easier to understand.
Understanding the UnboundLocalError
The core issue is Python's scoping rules. When you assign to a variable anywhere within a function, Python treats that variable as local to the function, unless you explicitly declare it as global
or nonlocal
. This local variable shadows any variable with the same name in an outer scope. The UnboundLocalError
occurs if you try to read from a local variable before you've assigned to it.
name = 'Alice' # Global variable
def example():
# ⛔️ UnboundLocalError: local variable 'name' referenced before assignment
print(name) # Trying to READ 'name' *before* assignment
name = 'Tom' # This assignment makes 'name' a LOCAL variable within example()
example()
Why it happens: The print(name)
line tries to access the local name
variable. The presence of the assignment name = 'Tom'
later in the function makes Python treat name
as local throughout the entire function. Because the local name
has not been assigned a value before the print
statement, the error occurs. The global name
defined outside the function is ignored (shadowed) within the function's scope.
Solution 1: Declare the Variable Before Using It (Within the Function)
The simplest fix, if you intend to work with a local variable, is to ensure you assign a value to it before you try to use it:
def example():
name = 'Tom' # Assign to 'name' FIRST
print(name) # Now it's safe to read 'name'
example() # Output: Tom
- This creates a local
name
that is completely separate from the globalname
. This is often the correct fix if you don't intend to modify a global variable.
Solution 2: Using the global
Keyword (Use Sparingly)
If you do intend to modify a global variable from within a function, use the global
keyword:
name = 'Alice' # Global variable
def example():
global name # Declare 'name' as global within the function
print(name) # Now refers to the global 'name'
name = 'Tom' # Modifies the global 'name'
example() # Output: Alice
print(name) # Output: Tom (global variable has been changed)
global name
: This line tells Python: "When I usename
in this function, I'm referring to the global variablename
, not a local one."- Now,
print(name)
correctly accesses the globalname
, andname = 'Tom'
modifies the global variable.
Overuse of global
variables can make code harder to understand and maintain. It's generally better to pass variables as arguments and return values (see Solution 3). Use global
only when truly necessary.
Solution 3: Passing Variables as Arguments (Recommended)
The best and cleanest way to avoid UnboundLocalError
(and avoid global variables) is to pass any needed variables as arguments to the function, and to return
any modified values:
name = 'Alice'
def example(first_name): # 'first_name' is a local parameter
full_name = first_name + ' Smith'
return full_name
result = example(name) # Pass the global 'name' as an argument
print(result) # Output: Alice Smith
print(name) # Output: Alice (global 'name' is unchanged)
- The
example
function now takesfirst_name
as an argument. Inside the function,first_name
is a local variable, completely independent of the globalname
. - The function returns the modified value, which you can then assign to a variable in the calling scope (if needed).
- This is the preferred approach because it makes your functions more:
- Self-contained: They don't rely on (or modify) external state.
- Reusable: You can call them with different inputs.
- Testable: Easier to test in isolation.
- Easier to understand: The data flow is explicit.
Solution 4: Returning Values from Functions
If you need a modified value outside of the function, return it:
name = 'Alice'
def example():
new_name = 'Tom' # The result of the function
return new_name # Return the new value
name = example() # The global variable is set to the returned value
print(name) # Output: Tom
- This approach avoids the
global
keyword and promotes immutability, resulting in cleaner, more predictable code.
Solution 5: Using the nonlocal
Keyword (Nested Functions)
The nonlocal
keyword is used in nested functions (functions defined inside other functions) to modify variables in the nearest enclosing scope that is not global.
def outer():
message = '' # 'message' is local to 'outer', but nonlocal to 'inner'
def inner():
nonlocal message # Declare 'message' as nonlocal
message = 'hello world'
print(message)
inner()
print(message) # Output: hello world
outer()
nonlocal message
: This line is crucial. It tells Python thatmessage
insideinner()
refers to themessage
variable in the enclosingouter()
function, not a new local variable withininner()
.- Without
nonlocal
, the assignmentmessage = 'hello world'
would create a new local variable namedmessage
insideinner()
, shadowing the outermessage
.
Best Practices to Avoid UnboundLocalError
- Pass arguments, return values: This is the most important practice. Favor passing variables to functions as arguments and returning results, rather than modifying global variables directly.
- Initialize variables: Always assign a value to a variable before you try to read it within a function's scope.
- Use
global
sparingly: Only useglobal
when you absolutely must modify a global variable from within a function. Overuse ofglobal
leads to code that is hard to reason about. - Use
nonlocal
correctly: Usenonlocal
only in nested functions when you need to modify a variable in an enclosing (but non-global) scope. - Avoid shadowing Do not declare variables with the same names in inner and outer scope to avoid unexpected behaviour.
Conclusion
The UnboundLocalError
in Python arises from the interaction of local and global (or nonlocal) scopes.
By understanding Python's scoping rules, and preferring to pass arguments and return values rather than using global
or nonlocal
(unless strictly necessary), you can prevent this error and write cleaner and more maintainable Python code.
Always remember to initialize your local variables before attempting to use them.