If you try to reference a local variable before assigning a value to it within the body of a function, you will encounter the UnboundLocalError: local variable referenced before assignment.
The preferable way to solve this error is to pass parameters to your function, for example:
test_var = 0 def test_func(test_var): test_var += 1 return test_var test_func(test_var)
Alternatively, you can declare the variable as global to access it while inside a function. For example,
test_var = 0 def test_func(): global test_var test_var += 1 return test_var test_func()
This tutorial will go through the error in detail and how to solve it with code examples.
Table of contents
- What is Scope in Python?
- UnboundLocalError: local variable referenced before assignment
- Example #1: Accessing a Local Variable
- Solution #1: Passing Parameters to the Function
- Solution #2: Use Global Keyword
- Example #2: Function with if-elif statements
- Solution #1: Include else statement
- Solution #2: Use global keyword
- Summary
What is Scope in Python?
Scope refers to a variable being only available inside the region where it was created. A variable created inside a function belongs to the local scope of that function, and we can only use that variable inside that function.
A variable created in the main body of the Python code is a global variable and belongs to the global scope. Global variables are available within any scope, global and local.
UnboundLocalError: local variable referenced before assignment
UnboundLocalError occurs when we try to modify a variable defined as local before creating it. If we only need to read a variable within a function, we can do so without using the global keyword. Consider the following example that demonstrates a variable var
created with global scope and accessed from test_func
:
var = 10 def test_func(): print(var) test_func()
10
If we try to assign a value to var
within test_func
, the Python interpreter will raise the UnboundLocalError:
var = 10 def test_func(): var += 1 print(var) test_func()
--------------------------------------------------------------------------- UnboundLocalError Traceback (most recent call last) Input In [17], in <cell line: 6>() 4 var += 1 5 print(var) ----> 6 test_func() Input In [17], in test_func() 3 def test_func(): ----> 4 var += 1 5 print(var) UnboundLocalError: local variable 'var' referenced before assignment
This error occurs because when we make an assignment to a variable in a scope, that variable becomes local to that scope and overrides any variable with the same name in the global or outer scope.
var +=1
is similar to var = var + 1
, therefore the Python interpreter should first read var
, perform the addition and assign the value back to var
.
var
is a variable local to test_func
, so the variable is read or referenced before we have assigned it. As a result, the Python interpreter raises the UnboundLocalError.
Example #1: Accessing a Local Variable
Let’s look at an example where we define a global variable number. We will use the increment_func
to increase the numerical value of number
by 1.
number = 10 def increment_func(): number += 1 return number print(increment_func())
Let’s run the code to see what happens:
--------------------------------------------------------------------------- UnboundLocalError Traceback (most recent call last) Input In [19], in <cell line: 7>() 4 number += 1 5 return number ----> 7 print(increment_func()) Input In [19], in increment_func() 3 def increment_func(): ----> 4 number += 1 5 return number UnboundLocalError: local variable 'number' referenced before assignment
The error occurs because we tried to read a local variable before assigning a value to it.
Solution #1: Passing Parameters to the Function
We can solve this error by passing a parameter to increment_func
. This solution is the preferred approach. Typically Python developers avoid declaring global variables unless they are necessary. Let’s look at the revised code:
number = 10 def increment_func(number): number += 1 return number print(increment_func(number))
We have assigned a value to number
and passed it to the increment_func
, which will resolve the UnboundLocalError. Let’s run the code to see the result:
11
We successfully printed the value to the console.
Solution #2: Use Global Keyword
We also can solve this error by using the global
keyword. The global statement tells the Python interpreter that inside increment_func
, the variable number is a global variable even if we assign to it in increment_func
. Let’s look at the revised code:
number = 10 def increment_func(): global number number += 1 return number print(increment_func())
Let’s run the code to see the result:
11
We successfully printed the value to the console.
Example #2: Function with if-elif statements
Let’s look at an example where we collect a score from a player of a game to rank their level of expertise. The variable we will use is called score and the calculate_level
function takes in score
as a parameter and returns a string containing the player’s level
.
score = int(input("Enter your score between 0 and 100: ")) def calculate_level(score): if score > 90: level = 'expert' elif score > 70: level = 'advanced' elif score > 55: level = 'intermediate' return level
In the above code, we have a series of if-elif statements for assigning a string to the level
variable. Let’s run the code to see what happens:
print(f'Your level is: {calculate_level(score)}')
Enter your score between 0 and 100: 40 --------------------------------------------------------------------------- UnboundLocalError Traceback (most recent call last) Input In [12], in <cell line: 1>() ----> 1 print(f'Your level is: {calculate_level(score)}') Input In [11], in calculate_level(score) 7 elif score > 55: 8 level = 'intermediate' ----> 9 return level UnboundLocalError: local variable 'level' referenced before assignment
The error occurs because we input a score
equal to 40
. The conditional statements in the function do not account for a value below 55
, therefore when we call the calculate_level
function, Python will attempt to return level
without any value assigned to it.
Solution #1: Include else statement
We can solve this error by completing the set of conditions with an else statement. The else statement will provide an assignment to level for all scores lower than 55
. Let’s look at the revised code:
score = int(input("Enter your score between 0 and 100: ")) def calculate_level(score): if score > 90: level = 'expert' elif score > 70: level = 'advanced' elif score > 55: level = 'intermediate' else: level = 'beginner' return level
In the above code, all scores below 55
are given the beginner level. Let’s run the code to see what happens:
print(f'Your level is: {calculate_level(score)}')
Enter your score between 0 and 100: 40 Your level is: beginner
Solution #2: Use global keyword
We can also create a global variable level
and then use the global
keyword inside calculate_level
. Using the global
keyword will ensure that the variable is available in the local scope of the calculate_level
function. Let’s look at the revised code.
score = int(input("Enter your score between 0 and 100: ")) level = 'beginner' def calculate_level(score): global level if score > 90: level = 'expert' elif score > 70: level = 'advanced' elif score > 55: level = 'intermediate' return level
In the above code, we put the global statement inside the function and at the beginning. Note that the “default” value of level
is beginner
and we do not include the else
statement in the function. Let’s run the code to see the result:
print(f'Your level is: {calculate_level(score)}')
40 Your level is: beginner
Summary
Congratulations on reading to the end of this tutorial! The UnboundLocalError: local variable referenced before assignment occurs when you try to reference a local variable before assigning a value to it. Preferably, you can solve this error by passing parameters to your function. Alternatively, you can use the global keyword.
If you have if-elif statements in your code where you assign a value to a local variable and do not account for all outcomes, you may encounter this error. In which case, you must include an else statement to account for the missing outcome.
For further reading on Python code blocks and structure, go to the article: How to Solve Python IndentationError: unindent does not match any outer indentation level.
Go to the online courses page on Python to learn more about Python for data science and machine learning.
Have fun and happy researching!
As a new Python programmer, I ran into three common language gotchas: passing variables by reference, unbound local errors, and mutable default arguments. Hopefully this post will help shed some light on these gotchas and save you the time I spent debugging them. However, what I gained by attempting to understand these gotchas was a wonderful opportunity to take a deep dive into a new language and explore some of its under-the-hood behavior.
Passing By Reference
I encountered the pass-by-reference gotcha when I first tried to play the game loop. My game was able to ask the user for input, set the size of the board, create a board, and accurately create a human and computer player. However, when I tried to place the first move, my move was not marked in the one square I had picked, but in three squares.
My game was marking the entire column.
In other words, instead of looking like the image on the left, my board looked like the image on the right:
After some research and debugging, I discovered that this behavior was thanks to Python’s pass by reference characteristic. My board had been created using the following function:
[[None * row_size] * row_size]
Here when creating a board with a row_size of 3, one might expect to get 9 distinct None objects. However, when building the board using this function in Python, we start off with a reference to a list, and then replicate that reference 3 times. Hence when we change the value in one of the lists, we change the value in all 3 lists.
None itself is Python’s null equivalent. For more about None see the section directly below.
A Brief Look At None In Python
- None is an object — or to be more specific, a Singleton class. Not a primitive type, such as int, or True and False.
- In Python 3.x, the type object was changed from type ‘NoneType’ to class ‘NoneType’. However the behaviour of None has remained the same from 2.x to 3.x.
- Because None is an object, we cannot use it to check if a variable exists. It is a value/object, not an operator used to check a condition.
In this REPL—Click on the Play button to run tests, or toggle lines 24 and 25, and then run the code to view the Pass-by-Reference gotcha
Python Scope
To discuss Unbound Local Errors, I thought it might be helpful to touch upon Python scoping first. Scoping in Python follows the LEGB Rule: Local -> Enclosed
-> Global -> Built-in. Here we work our way from the inside out, from local to built-in.
- Local scope refers to a variable that is defined within a function body. A variable is always checked for in local scope first.
In the repl below, if we toggle the lines of code so that line 8 is off, we’ll see that our outer function executes and prints the local version of variable my_var, returning a value of my inner/local variable. - Enclosed scope is created when a function wraps another function—here the outer function is the enclosing scope. Back in our repl, we can now comment line 7 and uncomment line 8. On line 8, the nonlocal keyword will now point Python to using the variable initialized on line 5, in the outer enclosing scope. Thus we will see my enclosed variable printed to the console.
- Global—as we can imagine, variables assigned at the top-level of a module file are global in scope. line 13 of the repl prints my_var from line 2, as it is unable to access the various other instantiations of my_var that are all within enclosed or local scopes. Similar to the nonlocal keyword, we can use the global keyword to initialize a variable to the global scope from an enclosed or local context.
- Built-ins—no surprises here either, built-ins refer to names preassigned in Python’s built-in names module, such as Math, open, range, and SyntaxError.
In this REPL—Click on the Play button to run tests, or toggle lines 15, 24, and 25, and then run the code to learn more about how Python’s LEGB scoping works
Unbound Local Error
Let’s say we define a variable my_var in the global scope and set it to 5. If we then create a function and attempt to assign a new value to the same variable, but within the function’s local context, we’ll get an UnboundLocalError returned to us.
The above error occurs because, when we make an assignment to a variable in scope, that variable becomes local to that scope and shadows any similarly named variable in the outer scope. In the context of the repl below, on line 7 we are in effect initializing a new variable my_var to a value of += 1, and an error is generated. This error is particularly common when working with lists, as shown in the last example in the repl, and one way to solve accessing a variable declared in the global context is to refer to it as global, as seen in the method on line 10 of the repl.
In this REPL—Click on the Play button to run tests, or edit the code in functions bar and foo2 to play with Unbound Local Errors
Mutable Default Arguments
For my second bug, a gotcha showed up in my implementation of the Minimax algorithm. My code was displaying some strange behavior.
My first test was set up to check that Minimax chose the only open spot, in this case a 5.
After that, the next test checked if, when given a choice between two open spots, Minimax selected the winning move as opposed to a blocking move. In this case the open spots were 8 and 9, counting from a 1-indexed array.
A third test checked whether, when given a choice of multiple open spots, Minimax still chose the winning move. Here the open spots in the 1-indexed array included 4, 6, 7, 8, 9.
Strangely enough, 5 kept showing up as the selected move in all three of these tests, even though 5 was only a valid option in my very first test, and not a valid option in the other two mentioned. It looked like the open spot from my first test was somehow persisting into my next two tests.
Here my mentor alerted me to read up on Python Gotchas, of which Mutable Default Arguments1 was one.
According to the article,
A new list is created once when the function is defined, and the same list is used in each successive call.
Python’s default arguments are evaluated once when the function is defined, not each time the function is called (like it is in say, Ruby). This means that if you use a mutable default argument and mutate it, you will and have mutated that object for all future calls to the function as well.
Here was the offending line itself:
def minimax(self, game, depth=0, scores_map={}):
The same goes for any default arguments in a function call that are set to mutable data types in Python. A quick search for mutable types in Python tells us:
Objects of built-in types like int, float, bool, str, tuple, and unicode are immutable. Objects of built-in types like list, set, and dict are mutable. Custom classes are generally mutable.
A table to illustrate might be simpler, see below.2
My scores map was being mutated with a 5 the first time it was called and then it held on to the 5 for all future calls to that function as well.
To avoid this common gotcha, the trick was to adjust my code to initialize the scores map variable to None instead of an empty dictionary in the function call, and then to check if scores_map needed to be initialized later within the body of the function:
def minimax(self, game, depth=0, scores_map=None):
if not scores_map:
scores_map = {}
In this REPL—Click on the Play button to run tests, or toggle the multiple tests on each of the map and array methods to view how this quirk is triggered
Mutable & Immutable Types In Python
Class | Description | Immutable | Mutable |
---|---|---|---|
bool | Boolean value | x | |
int | Integer (arbitrary magnitude) | x | |
float | floating-point number | x | |
list | Mutable sequence of objects | x | |
tuple | Immutable sequence of objects | x | |
str | Character string | x | |
set | Unordered set of distinct objects | x | |
frozenset | Immutable form of set class | x | |
dict | Dictionary/Associative mapping | x |
The “local variable referenced before assignment” error occurs when you give reference of a local variable without assigning any value.
Example:
v1 = 1
def myfunction():
if v1 == 0:
print("Value: Zero")
elif v1 == 1:
print("Value: One")
elif v1 >= 2:
print("Value: Greater then 1")
v1 = 0
myfunction()
Output:
UnboundLocalError: local variable “v1” referenced before assignment
Explanation:
In the above example, we have given the value of variable “v1” in two places.
- Outside the function “myfunction()”.
- And at the end of the function “myfunction()”.
If we assign a value of a variable in the function it becomes local variable to that function, but in the above example we have assigned the value to “v1” variable at the end of the function and we are referring this variable before assigning.
And the variable“v1” which we have assigned at the beginning of the code block is not declared as a global variable.
Solutions:
To avoid an error like “UnboundLocalError: local variable referenced before assignment” to occur, we have to:
- Declare GLOBAL variable
- Pass parameters with the function
Declare Global Variable
Code example with global variable:
v1 = 1
def myfunction():
global v1
if v1 == 0:
print("Value: Zero")
elif v1 == 1:
print("Value: One")
elif v1 >= 2:
print("Value: Greater then 1")
myfunction()
Output:
Value: One
Explanation:
As we know if we declare any variable as global then its scope becomes global.
Pass function with Parameters
Code example passing parameters with function:
def myfunction(v1):
if v1 == 0:
print("Value: Zero")
elif v1 == 1:
print("Value: One")
elif v1 >= 2:
print("Value: Greater then 1")
myfunction(10)
Output:
Value: Greater then 1
Explanation:
In the above example, as you can see, we are not using a global variable but passing the value of variable “v1” as a parameter with the function “myfunction()”.
Example 2
def dayweek(day):
if day == 7 or day == 6 or day == 0:
wd = 'Weekend'
elif day >= 1 and day <= 5:
wd = 'Weekday'
return wd
print(dayweek(10))
Output:
UnboundLocalError: local variable 'wd' referenced before assignment
Example 2.1
def dayweek(day):
if day == 7 or day == 6 or day == 0:
wd = 'Weekend'
elif day >= 1 and day <= 5:
wd = 'Weekday'
return wd
print(dayweek(1))
Output:
Weekday
In the «example2«, we have called a function “dayweek()” with parameter value “10” which gives the error but the same function with value “1” which runs properly in “example 2.1” and returns the output as “Weekday”.
Because in the above function we are assigning the value to variable “wd” if the value of variable «day» is the range from (0 to 7). If the value of variable «day» greater than «7» or lower then «0″ we are not assigning any value to variable «wd» That’s why, whenever the parameter is greater than 7 or less than 0, python compiler throws the error “UnboundLocalError: local variable ‘wd’ referenced before assignment”
To avoid such type of error you need assign the function variable which lies within the range or we need to assign some value like «Invalid Value» to variable «wd» if the value of variable «day» is not in range from (0 to 7)
Correct Example with Exception
def dayweek(day):
if day == 7 or day == 6 or day == 0:
wd = 'Weekend'
elif day >= 1 and day <= 5:
wd = 'Weekday'
else:
wd = 'Invalid Value'
return wd
print(dayweek(22))
Python treats variables referenced only inside a function as global variables. Any variable assigned to a function’s body is assumed to be a local variable unless explicitly declared as global.
Why Does This Error Occur?
Unboundlocalerror: local variable referenced before assignment
occurs when a variable is used before its created. Python does not have the concept of variable declarations. Hence it searches for the variable whenever used. When not found, it throws the error.
Before we hop into the solutions, let’s have a look at what is the global and local variables.
Local Variable Declarations vs. Global Variable Declarations
Local Variables | Global Variables |
---|---|
A local variable is declared primarily within a Python function. | Global variables are in the global scope, outside a function. |
A local variable is created when the function is called and destroyed when the execution is finished. | A Global Variable is created upon execution and exists in memory till the program stops. |
Local Variables can only be accessed within their own function. | All functions of the program can access global variables. |
Local variables are immune to changes in the global scope. Thereby being more secure. | Global Variables are less safer from manipulation as they are accessible in the global scope. |
Local Variable Referenced Before Assignment Error with Explanation
Try these examples yourself using our Online Compiler.
Let’s look at the following function:
# Variable decleration outside function (global scope) myVar = 10 def myFunction(): if myVar == 5: print("5") elif myVar == 10: print("10") elif myVar >= 15: print("greater than 15 ") # reassigning myVar in the function myVar = 0 myFunction()
Output
Explanation
The variable myVar
has been assigned a value twice. Once before the declaration of myFunction
and within myFunction
itself.
Solutions
Using Global Variables
Passing the variable as global allows the function to recognize the variable outside the function.
myVar = 10 def myFunction(): # Telling the function to consider myVar as global global myVar if myVar == 5: print("5") elif myVar == 10: print("10") elif myVar >= 15: print("greater than 15 ") myVar = 0 myFunction()
Output
10
Create Functions that Take in Parameters
Instead of initializing myVar
as a global or local variable, it can be passed to the function as a parameter. This removes the need to create a variable in memory.
# Passing myVar as a parameter in the function def myFunction(myVar): if myVar == 5: print("5") elif myVar == 10: print("10") elif myVar >= 15: print("greater than 15 ") myVar = 0 myFunction(10)
Output
10
UnboundLocalError: local variable ‘DISTRO_NAME’
This error may occur when trying to launch the Anaconda Navigator in Linux Systems.
Upon launching Anaconda Navigator, the opening screen freezes and doesn’t proceed to load.
$ anaconda-navigator
Traceback (most recent call last):
File "/home/user/anaconda3/lib/python3.7/site-packages/anaconda_navigator/widgets/main_window.py", line 541, in setup
self.post_setup(conda_data=conda_data)
...
...
UnboundLocalError: local variable 'DISTRO_NAME' referenced before assignment
Solution 1
Try and update your Anaconda Navigator with the following command.
conda update anaconda-navigator
Solution 2
If solution one doesn’t work, you have to edit a file located at
.../anaconda3//lib/python3.7/site-packages/anaconda_navigator/api/external_apps/vscode.py
After finding and opening the Python file, make the following changes:
In the function on line 159, simply add the line:
DISTRO_NAME = None
Save the file and re-launch Anaconda Navigator.
The program takes information from a form filled out by a user. Accordingly, an email is sent using the information.
from django import forms # Creating a Class for django forms class MyForm(forms.Form): name = forms.CharField(required=True) email = forms.EmailField(required=True) msg = forms.CharField( required=True, widget=forms.Textarea ) ... # Create a function to retrieve info from the form def GetContact(request): form_class = ContactForm if request.method == 'POST': form = form_class(request.POST) # Upon verifying validity, get following info and email with said info if form.is_valid(): name = request.POST.get('name') email = request.POST.get('email') msg = request.POST.get('msg') send_mail('Subject here', msg, email, ['[email protected]'], fail_silently=False) return HttpResponseRedirect('blog/inicio') return render(request, 'blog/inicio.html', {'form': form}) ...
Upon running you get the following error:
local variable 'form' referenced before assignment
Explanation
We have created a class myForm
that creates instances of Django forms. It extracts the user’s name, email, and message to be sent.
A function GetContact
is created to use the information from the Django form and produce an email. It takes one request
parameter. Prior to sending the email, the function verifies the validity of the form. Upon True
, .get()
function is passed to fetch the name, email, and message. Finally, the email sent via the send_mail
function
Why does the error occur?
We are initializing form
under the if request.method == “POST” condition statement. Using the GET request, our variable form
doesn’t get defined.
FAQs on Local Variable Referenced Before Assignment
How would you avoid using a global variable in multi-threads?
With the help of the threading module, you can avoid using global variables in multi-threading. Make sure you lock and release your threads correctly to avoid the race condition.
Conclusion
Therefore, we have examined the local variable referenced before the assignment Exception in Python. The differences between a local and global variable declaration have been explained, and multiple solutions regarding the issue have been provided.
Trending Python Articles
-
“Other Commands Don’t Work After on_message” in Discord Bots
●February 5, 2023
-
Botocore.Exceptions.NoCredentialsError: Unable to Locate Credentials
by Rahul Kumar Yadav●February 5, 2023
-
[Resolved] NameError: Name _mysql is Not Defined
by Rahul Kumar Yadav●February 5, 2023
-
Best Ways to Implement Regex New Line in Python
by Rahul Kumar Yadav●February 5, 2023