Unbound Local error in Python I can't shake! - python

http://pastie.org/1966237
I keep getting an unbound local error. I don't understand why it occurs, if the program is running right, it should go straight into the second assignment of the print_et_list function within the main function, looping itself without actually looping. The program only quits using sys.exit() in the hey_user function.
I included the whole program for context, it isn't too long. Let me know if you want to have a look at the text files I use in the program, however I'm sure it's unlikely that it is the source of the problem.

UnboundLocalError happens when you read the value of a local variable before you set it. Why is score a local variable rather than a global variable? Because you set it in the function. Consider these two functions:
def foo():
print a
vs
def bar():
a = 1
print a
In foo(), a is global, because it is not set inside the function. In bar(), a is local. Now consider this code:
def baz():
print a
a = 1
Here, a is set within the function, so it's local. But it hasn't been set at the time of the print statement, so you get the UnboundLocalError.

You forgot to pass score into hey_user().

Looks like it's probably the score variable. It's a local in main(), but you try to reference it in hey_user().

If you want to make score a global variable, be sure to declare it with the global statement:
def main ():
global score
score = 0
question, solution = print_et_list()
scoresofar = hey_user (solution)
print "\nYour score is now", scoresofar
question, solution = print_et_list()

Related

How do I make a variable created inside a function become global?

I have a function in a program that I`m working at and I named a variable inside this function and I wanted to make it global. For example:
def test():
a = 1
return a
test()
print (a)
And I just can`t access "a" because it keeps saying that a is not defined.
Any help would be great, thanks.
I have made some changes in your function.
def test():
# Here I'm making a variable as Global
global a
a = 1
return a
Now if you do
print (a)
it outputs
1
As Vaibhav Mule answered, you can create a global variable inside a function but the question is why would you?
First of all, you should be careful with using any kind of global variable, as it might be considered as a bad practice for this. Creating a global from a function is even worse. It will make the code extremely unreadable and hard to understand. Imagine, you are reading the code where some random a is used. You first have to find where that thing was created, and then try to find out what happens to it during the program execution. If the code is not small, you will be doomed.
So the answer is to your question is simply use global a before assignment but you shouldn't.
BTW, If you want c++'s static variable like feature, check this question out.
First, it is important to ask 'why' would one want that? Essentially what a function returns is a 'local computation' (normally). Having said so - if I have to use return 'value' of a function in a 'global scope', it's simply easier to 'assign it to a global variable. For example in your case
def test():
a = 1 # valid this 'a' is local
return a
a = test() # valid this 'a' is global
print(a)
Still, it's important to ask 'why' would I want to do that, normally?

How to refer global variable Python?

I am trying get the value of 'p1' variable outside a function. I have defined it as a global variable but I cannot reference is outside the function. It gives me error, "NameError: global name 'p1' is not defined"
Following is my code. Please let me know how can I get the value of global variable outside my function & also reference it in other function
#!/bin/usr/python
import subprocess
import string
def ping_check():
global p1
with open ('yst-host.txt') as hl:
for host in hl:
print host
p1 = subprocess.call("ping -c2 " + string.strip(host)+ "> /dev/null", shell=True)
print "Value of P1 is: ", p1
#ping_check()
print p1
In the code shown, you never actually call ping_check(), so your code is more or less equivalent to:
#!/usr/bin/env python
print p1
And then it's pretty clear that you're trying to use a global variable that doesn't exist yet.
Perhaps you should call your function? And make sure that hl is not empty? That way you'll have assigned to p1 at least once.
And for anyone wondering, here's a valid use of global in python. Note that we don't need to predefine a global variable, although that might be a good practice (so you can get a known value rather than a NameError).
def fun():
global p1
p1 = 3
fun()
print p1
This was going to be a comment, but it might actually be the answer.
As sharth points out, you don't need to predeclare a global variable to use it. However, it is added to globals() (and can be accessed without NameError outside the function) after you assign a value to it, not after the global statement. This means, that you should check if your code ever reaches the line with assignment.
Here's an example:
def more_fun():
global am_i_defined
print globals() #doesn't include am_i_defined
am_i_defined = True
print globals()
more_fun()
print am_i_defined

Python: Using a Variable Between Functions

There's something that's been bugging me (ha!) in Python (I use 2.7). It's that I get NameError: global name 'x' is not defined when I run this code:
def function1():
x = 1
return 0
def function2():
function1()
print(x)
return 0
function2()
It's not a serious problem for me, but I am genuinely curious about why this doesn't print 1. It makes sense and flows right in my mind. The function that defines the variable x is defined before it is called, and the function is called before print(x). I seriously am not seeing why this code doesn't work. Maybe the way I am thinking about this is flawed. Either way, why doesn't that code print 1? Thanks in advance for the help!
Yes, your thinking is flawed. The variable x defined in function1 is local to that function. It doesn't exist anywhere else. When you call one function with another, that doesn't mean that all the variables from the called function get dumped into the calling function. Only the return value is passed back. If you want to use x in the second function, you should return it from function1. (Even then, it won't create a variable called x in function2. It will only return the value, which you can then assign to a variable in function2 if you want, or print it, or whatever.)
in both functions use global x as the 1st line. this will make your code work. but, anyway, passing variables between functions is not a good practice. returning values and using parameters is a good practice.
if you are testing variable-sharing, you're using global variables. for that, unlike javascript, you NEED to use global x before trying to get it, as is in php (except that you don't get error in php unless using strict mode).
x scope is only inside function1() that's why you can't print x inside function2(), even if you're calling function1() (when you return from that function1(), x will be destroyed)
Because inside function1 scope variable x is local and function2 doesn't "see" this variable.

Globals as function input instead arguments

I'm just learning about how Python works and after reading a while I'm still confused about globals and proper function arguments. Consider the case globals are not modified inside functions, only referenced.
Can globals be used instead function arguments?
I've heard about using globals is considered a bad practice. Would it be so in this case?
Calling function without arguments:
def myfunc() :
print myvalue
myvalue = 1
myfunc()
Calling function with arguments
def myfunc(arg) :
print arg
myvalue = 1
myfunc(myvalue)
I've heard about using globals is considered a bad practice. Would it be so in this case?
It depends on what you're trying to achieve. If myfunc() is supposed to print any value, then...
def myfunc(arg):
print arg
myfunc(1)
...is better, but if myfunc() should always print the same value, then...
myvalue = 1
def myfunc():
print myvalue
myfunc()
...is better, although with an example so simple, you may as well factor out the global, and just use...
def myfunc():
print 1
myfunc()
Yes. Making a variable global works in these cases instead of passing them in as a function argument. But, the problem is that as soon as you start writing bigger functions, you quickly run out of names and also it is hard to maintain the variables which are defined globally. If you don't need to edit your variable and only want to read it, there is no need to define it as global in the function.
Read about the cons of the global variables here - Are global variables bad?
There are several reasons why using function arguments is better than using globals:
It eliminates possible confusion: once your program gets large, it will become really hard to keep track of which global is used where. Passing function arguments lets you be much more clear about which values the function uses.
There's a particular mistake you WILL make eventually if you use globals, which will look very strange until you understand what's going on. It has to do with both modifying and reading a global variable in the same function. More on this later.
Global variables all live in the same namespace, so you will quickly run into the problem of overlapping names. What if you want two different variables named "index"? Calling them index1 and index2 is going to get real confusing, real fast. Using local variables, or function parameters, means that they all live in different namespaces, and the potential for confusion is greatly reduced.
Now, I mentioned modifying and reading a global variable in the same function, and a confusing error that can result. Here's what it looks like:
record_count = 0 # Global variable
def func():
print "Record count:", record_count
# Do something, maybe read a record from a database
record_count = record_count + 1 # Would normally use += 1 here, but it's easier to see what's happening with the "n = n + 1" syntax
This will FAIL: UnboundLocalError: local variable 'record_count' referenced before assignment
Wait, what? Why is record_count being treated as a local variable, when it's clearly global? Well, if you never assigned to record_count in your function, then Python would use the global variable. But when you assign a value to record_count, Python has to guess what you mean: whether you want to modify the global variable, or whether you want to create a new local variable that shadows (hides) the global variable, and deal only with the local variable. And Python will default to assume that you're being smart with globals (i.e., not modifying them without knowing exactly what you're doing and why), and assume that you meant to create a new local variable named record_count.
But if you're accessing a local variable named record_count inside your function, Python won't let you access the global variable with the same name inside the function. This is to spare you some really nasty, hard-to-track-down bugs. Which means that if this function has a local variable named record_count -- and it does, because of the assignment statement -- then all access to record_count is considered to be accessing the local variable. Including the access in the print statement, before the local variable's value is defined. Thus, the UnboundLocalError exception.
Now, an exercise for the reader. Remove the print statement and notice that the UnboundLocalError exception is still thrown. Can you figure out why? (Hint: before assigning to a variable, the value on the right-hand side of the assignment has to be calculated.)
Now: if you really want to use the global record_count variable in your function, the way to do it is with Python's global statement, which says "Hey, this variable name I'm about to specify? Don't ever make it a local variable, even if I assign to it. Assign to the global variable instead." The way it works is just global record_count (or any other variable name), at the start of your function. Thus:
record_count = 0 # Global variable
def func():
global record_count
print "Record count:", record_count
# Do something, maybe read a record from a database
record_count = record_count + 1 # Again, you would normally use += 1 here
This will do what you expected in the first place. But hopefully now you understand why it will work, and the other version won't.
It depends on what you want to do.
If you need to change the value of a variable that is declared outside of the function then you can't pass it as an argument since that would create a "copy" of that variable inside the functions scope.
However if you only want to work with the value of a variable you should pass it as an argument. The advantage of this is that you can't mess up the global variable by accident.
Also you should declare global variable before they are used.

why these two programs result in diffrent results?

I have changed the value of 'a' before calling the function get_size() in both the programs
1st:
a=''
def get_size(b):
size=len(b)
return size
def main():
a='qwerr'
print 'the size of a is:',get_size(a)
if __name__=='__main__':
main()
console:the size of a is: 5
2nd:
a=''
def get_size():
size=len(a)
return size
def main():
a='qwerr'
print 'the size of a is:',get_size()
if __name__=='__main__':
main()
console:the size of a is: 0
In the first program, you are setting a at global scope, then resetting it to another string in def main scope.
It's a scope problem. The first program creates a in the local scope of main(), then passes that to get_size(), while the second program references the global a from within get_size(). If you want the second one to work as you expect, you need to make a global within main() scope.
main()
global a
a = 'qwerr'
print 'the size of a is:',get_size()
As pointed out in the comments, a main()-scoped a is created in both versions, but since get_size() in version 1 references the global a, setting the local a in main() has no impact on what get_size() operates on.
Really though, you should try not to use globals, in part to avoid exactly the ambiguity you are experiencing here.
In the second program, get_size method will check for 'a' value in its local scope but as it is not there in it's local scope and then it will verify in it's global scope

Categories