Why do I get an UnboundLocalError in my function? - python

I am newbie, trying to learn python.
According to this article http://www.tutorialspoint.com/python/python_modules.htm, I get an UnboundLocalError in the following code but I don't understand why:
Money = 2000
def AddMoney():
# Uncomment the following line to fix the code:
# global Money
Money = Money + 1
print Money
AddMoney()
print Money
But the below code works fine
Money = 2000
def AddMoney():
# Uncomment the following line to fix the code:
# global Money
Money = 1
print Money //2000
AddMoney()
print Money //2000
They explain things in the article, but I'm still not sure that I understand. Why do I get an UnboundLocalError in the first example, but not in the second?

As you've mentioned, the article does explain a little bit, but not very much about why.
When you're programming and you write the name of something, the computer has to know where to go look for that thing. The where is a term known as scope. All variables in Python have a scope. Here are some examples to illustrate:
from __future__ import print_function
value = 'blue'
def fun():
value = 'yellow'
print("Value in function: ", value)
print("Value before: ", value)
fun()
print("Value after: ", value)
# Output:
#
# Value before: blue
# Value in function: yellow
# Value after: blue
In this script you define value to be 'blue'. Then inside the function you set it to 'yellow'. But why does it not stay 'yellow' after the function is called? Scoping.
When you define value = 'yellow' in your function, the name value is bound to 'yellow' only inside the current block (a function, in this case). You'll hear the terms shadow or hiding to explain what's happening here, because that's effectively what's happening. You are hiding the original value with a new value, but once your function ends, so does your new value.
You can see this by using the globals()and locals() builtins
from __future__ import print_function
from pprint import pprint
value = 'blue'
def fun():
print("Globals in function: ")
pprint(globals())
print("Locals in function: ")
pprint(locals())
value = 'yellow'
print("Value in function: ", value)
print("Globals at end of function: ")
pprint(globals())
print("Locals at end of function: ")
pprint(locals())
print("Globals before function: ")
pprint(globals())
print("Locals before function: ")
pprint(locals())
print("Value before: ", value)
fun()
print("Value after: ", value)
print("Globals after function: ")
pprint(globals())
print("Locals after function: ")
pprint(locals())
Now, python has a global keyword that allows you to tell Python that instead of looking inside the local scope, you want to look in the global scope for these variables:
from __future__ import print_function
value = 'blue'
def fun():
global value
value = 'yellow'
print("Value in function: ", value)
print("Value before: ", value)
fun()
print("Value after: ", value)
# Output:
#
# Value before: blue
# Value in function: yellow
# Value after: yellow
In Python, it will try to resolve names first in the local scope, and then in the global scope, which means you can write something like this:
from __future__ import print_function
value = 'blue'
def fun():
print("Value in function: ", value)
print("Value before: ", value)
fun()
print("Value after: ", value)
# Output:
#
# Value before: blue
# Value in function: blue
# Value after: blue
But if that works, then why doesn't your first example, with Money = Money + 1? The answer is simple, though perhaps unexpected:
Because you are trying to redefine Money in your function (the Money = part), it cannot go look for your variable in the global scope, because it doesn't know if you meant to use the global version or the local version, and you get an UnboundLocalError. So you need to tell Python what you mean: Did you want to use the global version, or should you be using something locally?
By declaring global Money, you tell Python explicitly that you want to use the global version. But if you want to use something local then you'll need to use a different name, or define Money before you try to use it as in Money + 1.

AddMoney doesnt know Money variable has been defined before so it crashes with local variable 'Money' referenced before assignment thinking is a local variable which should have been declared inside AddMoney class.
If you uncomment the global Money line inside AddMoney class it will work.
Also the second example is working because in the line Money = 1 you are not using Money variable, you are just asigning a value to Money and overriding the global value

Preface: I'm going with the assumption that the space before Money = 2000 in the second snippet is a mistake, because whitespace is significant in Python, but I don't think it actually makes a change here.
In your first example Money = Money + 1 is trying to read from a variable and then assign a new value to it. Money doesn't exist in the local scope, so it throws an error. The second example is only assigning a value to a variable. AddMoney() creates a local variable called Money and then does nothing with it. The Money from the outer scope is unchanged, as your comments indicate.

The reason is the conflict between globals and local scope variables
(As others have said)
declaring money as global in the function shall do good
Money = 2000
def AddMoney():
global Money
Money = Money + 1

In both functions, you need to delete the hashtag in the line '# global Money' in order for the function to work properly. Hashtags in Python cause the rest of the line to be 'commented', which are lines that will not affect your code and instead act as notes to the developer.
This code will work properly:
Money = 2000
def AddMoney():
# Uncomment the following line to fix the code:
global Money
Money = Money + 1
print Money
AddMoney()
print Money
In your second piece of code, the function isn't redefining the variable money outside of the function's environment, so while an error is not appearing, you are not accomplishing what you want with the code. Again, deleting the hashtag (uncommenting) will solve your problem. This code will work:
Money = 2000
def AddMoney():
# Uncomment the following line to fix the code:
global Money
Money = 1
print Money //2000
AddMoney()
print Money //2000
Hope this helps!

Related

Python: Using a variable with a value defined in 1 function, being used in another function

def getValidSeason( ):
season = input ( "Enter a season between 1980 and 2016: " )
while season < 1980 or season > 2016:
season = input ( "Enter a season between 1980 and 2016: " )
return season
def getValidDriver( ):
driver = input ( "Enter a driver's name: " )
# This is where the first problem is
# I want to be able to use the value of season from getValidSeason( )
# in the getValidDriver( ) function
while getValidSeason( ) != 1980 and driver != "Steve Park" or driver != "Jamie McMurray"
driver = input ( "Enter a driver's name: " )
return driver
def printResults( ):
# Basically the same as before, I want the value of the driver
# variable defined in the getValidDriver( ) to be used in the print results( ) function
print ( "The driver being selected is",getValidDriver( ) )
def main( ):
# I don't believe my question has anything to do with what you put in the main function , but I might be wrong.
I looked around and could not find help with my problem. I am sorry if there is already a solution out there, but I couldn't find anything. Maybe I was wrong. I'm struggling with programming. I did find a question on this website that was basically the same question but it involved an earlier version of Python, and they were talking about stuff that is no longer used in the current version of Python.
Thanks for the help.
Your code is very close (although I don't understand the condition of the while loop). I cleaned up the formatting such as lowercase R in return and spacing conventions in function calls. I changed the inputs to convert to ints in getValidSeason() since you are trying to get strings. I also added some print lines in the while loop for my own benefit.
Also importantly, you don't need a main here (although it would be ok), but you do need to call your printResults() function to get the code running (the very last line of code). Let me know if you have questions about something I did or didn't explain.
CODE:
def getValidSeason():
season = int(input("Enter a season between 1980 and 2016: "))
while season < 1980 or season > 2016:
season = int(input("Enter a season between 1980 and 2016: "))
return season
def getValidDriver():
driver = input("Enter a driver's name: ")
season = getValidSeason()
# This is where the first problem is
# I want to be able to use the value of season from getValidSeason( )
# in the getValidDriver() function
#EDIT: Not sure what logic you actually want here
while season != 1980 and driver != "Steve Park" or driver != "Jamie McMurray":
print('Last driver selected was: '+str(driver))
print('Last season selected was: '+str(season))
driver = input("Enter a driver's name: ")
season = getValidSeason()
return driver
def printResults():
# Basically the same as before, I want the value of the driver
# variable defined in the getValidDriver() to be used in the print results() function
valid_driver = getValidDriver()
print ("The driver being selected is",valid_driver)
printResults()
EDIT:
#This is Function "func1"
#It takes the variable "name" as its one and only argument
# and it adds '_1' to the end of it before returning name
def func1(name):
print('In func1 you passed in the name: '+name)
print('Adding "_1" to the end of the name')
name = name+'_1'
print('In func1 your new name is: '+name)
print('')
return name
#Just like func1
def func2(name):
print('In func2 you passed in the name: '+name)
print('Adding "_2" to the end of the name')
name = name+'_2'
print('In func2 your new name is: '+name)
print('')
return name
#Function that will ask the user for a name
#Doesn't take any arguments
#Returns the name
def get_name():
name = 'Steve' #<-- just set to always be steve for now (you can replace this w/ an 'input()')
print('Your original name was '+name)
print('')
name_f1 = func1(name) #<-- original name w/ '_1' added to the back
name_f2 = func2(name) #<-- original name w/ '_2' added to the back
name_f1_f2 = func2(name_f1) #<-- orig name w' '_1_2' added to the back
name_f2_f1 = func1(name_f2) #<-- orig name w' '_2_1' added to the back
return name
#Call the get_name function which will then call func1 and func2
get_name()
You probably want to save the functions' return value in a variable in the other function. Here's an example for printResults:
def printResults():
driver = getValidDriver()
# you can now use the 'driver' variable
print("The driver being selected is", driver)
Python Course Shows that you simply need to add the global flag if you want to declare the variable as global inside a function. to further quote Python Course :
"The way Python uses global and local variables is maverick. While in many or most other programming languages variables are treated as global if not otherwise declared, Python deals with variables the other way around. They are local, if not otherwise declared."
I am not sure if this will resolve your issue completely since you are returning the variable back out. Thats simply the first thing I noticed about your code.
"I believe I understood the code you just put up. I'm getting my new keyboard in a couple days so I can't type the code yet, but I think I understand what is going on there. I don't think it answered my question though, unless I am missing something. Let's say in get_name( ) it asks the user to type in a name, and the user types in Tom. The variable that stores this is called name1. Now I want to use the variable name1 in multiple different functions, but I want the value of name1 to automatically be = to the user input defined in get_name( ) without the user having to type it again.
I think I could ask the user the question by using a global variable to ask it, but isn't it proper coding to have it inside a function? Functions are supposed to do 1 thing each and be used to break the program up. Putting the question outside of a function I think you aren't supposed to do. "
I read your comments on the comment section. I posted them above. I think the only thin you can do is call the function or created a global variable.

Can i have multiple global varibles within a python script?

Is it possible to have more than one global variable within a python script?
import os,csv,random
def user():
global Forname
Forname = input('What is your forname? ').capitalize()
while True:
try:
global answerr
answerr = input('Welcome to the phone troubleshooting system '
'\nApple\nSamsung '
'\nOut of the following options enter the name of the device you own ').lower()
except ValueError:
continue
if answerr in ('apple','samsung'):
break
myfile = open(answerr+'_device.csv','r')
answer = input(Forname + ', do you have anymore problems? ').lower()
if 'yes' in answer:
#do whatever
else:
#do whatever
Using the global variable 'answerr' I'd like to open a csv file, and the refer to the user with the forname they input, but I want to use them multiple times through out my code within def functions. I apologise in advance if you do not understand what I'm asking, I'm relatively new to coding given the fact that I'm still a school student.
Of course it's possible. But there is absolutely no reason to use any global variables in this code, let alone multiple.
The point of a function is that it can return a value:
def user():
forename = input('What is your forename? ').capitalize()
return forename
Can I have multiple global variables within a Python script?
Yes and here's how:
When you're assigning any variable at the top-level of a module like: n = "Stackoverflow!" then your variable is global automatically. So let's say we have this modules:
#globals.py
x = 2 + 2
y = 5 + x
both x and y are global variables which means they're accessible to functions, classes and so on. *Just remember any assignment at the top-level of a module is actually global (this is what we call a global scope and it can contain as much variables as your memory allows). This is just like the code you posted. However, what we cannot have is same named variables in any scope:
same = 20
same = "s"
print(same)
will print s, not 20.
Hope you'll find this helpful :-)

python summing random integers

#Use main and a void function named randnums.
#randnums takes no arguments and return none.
#The randnums function generates 6 random integers between 1 and 9.
#The total should be printed on a new line.
#Main should call the randnums function.
import random
total=0
def main():
randnums()
def randnums():
for nums in range(6):
nums=random.randrange(1,10)
total=total+nums
print(nums,end=' ')
print("\nThe total is:",total)
main()
I keep getting:
local variable 'total' referenced before assignment
Or when total=nums it only shows the last int generated.
Can someone please explain to a beginner what I'm doing wrong?
When you assign to a variable inside a function, Python interprets it as local variable to that function. So when you do -
total=total+nums
You are actually trying to access the local variable total before defining it.
Based on your program, does not look like you need total to be a global variable, you can simply define it as 0 at the start of randnums() . Example -
def randnums():
total = 0
for nums in range(6):
You are facing problem because of variable scope.
total=total+nums
Notice that line, in your local scope, total doesn't exist but you are trying to get it's value and then add some num with it, which is the cause of your error.
If you really want to use it, use it like below:
global total
total=total+nums
So, that it recognises the global total variable.

local variable 'location' referenced before assignment-text adventure

I am working on a text adventure as my first python project. I am using a template, (coping code from youtube tutorial). but instead of creating a game loop, I want it to be a function, to be executed when the player types in a command. (that part is working).
Here is the code from the tutorial:
Text_Adventure
bridge = ("Bridge", "You are on the bridge of a spaceship, sitting in the captains chair. ")
readyRoom = ("Ready Room" , "The captains ready room ")
lift = ("Lift" , "A turbolift that takes you throughout the ship. ")
transitions = {
bridge: (readyRoom, lift),
readyRoom: (bridge,),
lift: (bridge,)
}
location = bridge
while True:
print (location[1])
print ("You can go to these places: ")
for (i, t) in enumerate(transitions[location]):
print (i + 1, t[0])
choice = int(input('Choose one: '))
location = transitions[location][choice - 1]
That part works okay, but when I try to turn it into a function:
Text_Adventure
bridge = ("Bridge", "You are on the bridge of a spaceship, sitting in the captains chair. ")
readyRoom = ("Ready Room" , "The captains ready room ")
lift = ("Lift" , "A turbolift that takes you throughout the ship. ")
transitions = {
bridge: (readyRoom, lift),
readyRoom: (bridge,),
lift: (bridge,)
}
location = bridge
def travel():
print (location[1])
print ("You can go to these places: ")
for (i, t) in enumerate(transitions[location]):
print (i + 1, t[0])
choice = int(input('Choose one: '))
location = transitions[location][choice - 1]
travel()
I get the error message:
UnboundLocalError: local variable 'location' referenced before assignment
I know that the best way to learn something is by finding the answer yourself. I have been searching for awhile now and am not getting anywhere, Any help would be greatly appreciated, Thank you.
This can be simplified quite a bit:
>>> a = 1
>>> def foo():
... print a
... a = 3
...
>>> foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in foo
UnboundLocalError: local variable 'a' referenced before assignment
What's happening
When python sees a the first time in the function, it is a non-local variable (in this case, a global). The second time though, since you're assigning to it, python thinks it is a local variable -- But the name is already taken by a global variable which leads to the error.
There are a few workarounds -- You can declare a as global so that python will know that when you say a = 3, you mean that the global variable a is 3. Personally though, I would advise you beat on the code some more so that you no longer need a global variable. 99 times out of 100, if you're using global, there's probably a better way to refactor the code so you don't need it.
If you write to a global variable you should use global to declare it.
instead this:
def travel():
put this:
def travel():
global location
Thank you for the help, I dont think Ill keep it like this, but it works for now:
#Simplefied version:
a = 1
def foo():
global a
a = 3
print a
def getFoo():
print a
print "foo results: "
foo()
print "getFoo results: "
getFoo()
Prints:
foo results:
3
getFoo results:
3
I was having trouble calling "a" from another function, that's why I displayed the function and the result separately. Its working for now, Thank You

Python, Functions changing values

So I am having trouble getting this system to work, and I can't be sure that I'm asking the right question, but here is what is happening and what I want to happen.
money = 1
def Stats():
print
print "money " + str(money)
def gainM():
money + 2
Stats()
if money == 1:
gainM()
Now what happens when it goes to print money, the value is still 1 even though I add 2 to the value. (code is not a copy of my actual program, but an example to show what is happening.)
money + 2 is a no-op. You actually have to assign money to a new value
money = money + 2
# or
money += 2
But then you'll find you get an error - you can't assign to variables outside a function scope. You can use the global keyword:
global money
money += 2
This will allow you to change the value of money within the function.
However, the recommended way is passing money as a parameter:
def gainM(money):
money += 2
Stats()
return money
if money == 1:
money = gainM(money)
If you're using the second option (which you should be), you also need to change your Stats function, to have a money parameter as well.
def Stats(money):
print
print "money " + str(money)
Otherwise the function will print 1 instead of 3.
Another recommendation - use string formatting.
'money %d' % money # the old way
'money {}'.format(money) # the new and recommended way
Now you pass money into the Stats function.
def gainM(money):
money += 2
Stats(money)
return money
You need to assign the new value to money. Like so:
money = money + 2
Or the shorthand form:
money += 2
Also, if the variable is outside your function, you need to declare it global(so it doesn't instead create a local variable)
So you end up with:
def gainM():
global money
money += 2
Stats()
Edit: just to clarify, I'm not saying you should use global variables. In general they are a bad idea(though they may be useful in some situations). However, that's what this particular example needs to work. Chances are, however, that what you want is probably a class with instance variables that the methods of that class modify. However, given that you don't seem to have grasped the basics of the language yet, take things one step at a time and don't worry about any words in my previous sentences you didn't understand for now :)

Categories