Im getting the referenced before assignment error - python

I am making a system that reads a file, moves it to a new file, and moves it back with a new variable at the end of each line.
It is saying that linesRead is referenced before assignment
import random
handVar = 0
handList = []
linesRead = 'test'
def dealCards(numPlayers, cardList):
random.shuffle(cardList)
for i in range(0, numPlayers):
poppedCard1=cardList.pop()
poppedCard2=cardList.pop()
hand = (poppedCard1 + ', ' + poppedCard2)
handList.append(hand)
cardFile = open('players.txt', 'r')
playerLines= cardFile.readlines()
cardFile.close()
for i in playerLines:
for f in i:
linesRead = linesRead + f
print(linesRead)
tempFile = open('tempFile.txt', 'w')
tempFile.write(playerLines)
tempFile.close
tempFile = open('tempFile.txt', 'r')
playerFile = open('players.txt', 'a')
for i in tempFile:
newLine= (i + ', ' + handList[handVar] + ', ' + handList[handVar+1])
playerFile.write(newLine)
handVar = handVar + 2

The reason for the error scopes of the variables.
You are defining the variable linesRead in the global scope (outside of the function). It is available inside the function as long as you do not try to reassign the variable. When you reassign, Python treats is as a local scope variable (the scope of the function), and since you are using it in the reassignment, it is not able to find the assignment of that var in local scope.
Let's look at the following examples:
num = 42
def foo():
print(num)
foo()
Output:
42
In this case num is a variable defined in the Global scope (aka. module scope). It is defined at the top level of the module and is available in the local scopes of functions.
But the things are different in the following example:
num = 42
def foo():
print(num)
num = 12
foo()
Output:
UnboundLocalError: local variable 'num' referenced before assignment
What happens in this case is Python making the num a variable of the local scope of the foo function. That is it has nothing to do with the num of the global scope. Since I'm trying to print it before I assign it a value, it gives me an error.
Things are different when I move the assignment above of the print function call:
num = 42
def foo():
num = 12
print(num)
foo()
Output:
12
Same happens in this case, but since num is already assigned in the local scope by the time I'm calling the print, it won't error.
Solution
One of the solutions is to include the var as a global in the function. Something like this
def dealCards(numPlayers, cardList):
global linesRead
it will make the var from global variable available at the local scope.
But in this case if you modify the var in the function, the one at the module level will be changed as well
num = 42
def foo():
global num
print(num)
num = 12
foo()
print(num)
Output
42
12
Here is a fine, detailed read on Python scopes:
https://realpython.com/python-scope-legb-rule/

Related

Why is this variable considered local?

I have this code:
with open("01-1-input.txt", "r") as f:
inputs = [int(i[:-2] if i[-1] == "n" else i) for i in f.readlines()]
total_mass = 0
def calculate_fuel_for_mass(mass):
fuel_for_mass = mass // 3 - 2
if fuel_for_mass > 0:
total_mass += fuel_for_mass
calculate_fuel_for_mass(fuel_for_mass)
else:
return 0
for i in inputs:
calculate_fuel_for_mass(i)
print(total_mass)
And it's throwing an UnboundLocalError: local variable 'total_mass' referenced before assignment.
Why is that? I thought any variable declared in the main scope is global?
The line
total_mass += fuel_for_mass
can be thought of as equivalent to
total_mass = total_mass + fuel_for_mass
Given a setup like this, python sees an assignment happening to a variable in local scope (inside the function).
A minimal demonstration of this behaviour can be seen as follows:
var = 42
def f():
var = var + 1
# var += 1 would also show the same behaviour
f() #UnboundLocalError: local variable 'var' referenced before assignment
Python infers that there is a local variable total_mass because it sees an assignment to the variable in the local scope.
However, the local variable total_mass has not been assigned a value,
and so you see the error as shown.
You can use the global keyword before the assignment to access the variable in the global scope as follows
var = 42
def f():
global var
var = var + 1
f() #var is now 43 in global scope
At the beginning of your function, put
global total_mass
For this, you have to know how variable scopes and mutable/immutable types in Python work. The int type is immutable, so when you try to "modify" it in the function, you essentially create a new variable with local scope. As the compiler can see that the variable is going to be local to the function, it assumes that you want to access the local variable in the first place, rather than the global one. It is possible to declare the variable global using the global keyword, however it's considered bad practice, as the program becomes harder to read and to debug.

python: name resolution clarification?

I'm reading the python reference name resolution, which reads
A class definition is an executable statement that may use and define names. These references follow the normal rules for name resolution with an exception that unbound local variables are looked up in the global namespace.
Based on that, I would expect the following code
x = 10
def f():
x = 5
class Test:
y = x
return Test
print(f().y)
to print 10, however it prints 5. is this a mistake in the reference, or am I misunderstanding something?
In this case, 'normal' rules apply:
x = 'global'
def f():
x = 'defined in f'
class Test:
print(x) # not local, normal rules apply
f()
# defined in f
In this second case, we would expect an UnboundLocalError: local variable 'x' referenced before assignment if we were inside a function:
x = 'global'
def f():
x = 'defined in f'
class Test:
print(x) # unbound local at this time
x = 'assigned in Test'
print(x)
But for the first print(x), x will be taken from the global namespace:
f()
# global
# assigned in Test
I think #khelwood gave the answer. The value in the variable:
Test.y
is an integer that you define, but you never give the x = 10 to the function.
Maybe what you want is actually:
x = 10
def f(x=5):
class Test:
y = x
return Test
print(f().y) #print default 5
print(f(x).y)
The last line print 10 since x is given to the function, and therefore the class set y as x
You can see the exception by wrapping f in another function that defines a local variable x:
x = 10
def g():
x = 7
def f():
class Test:
y = x
return Test
return f()
print(g().y)
Now the output is 7, as the lookup of x in the class statement bypasses the local scope of g to find the global value of x.
In your code, because class establishes a new namespace, but not a new scope, the value of x is taken from the current local scope, that of f.

Python: why global variable act as local variable in method? [duplicate]

How do global variables work in Python? I know global variables are evil, I'm just experimenting.
This does not work in python:
G = None
def foo():
if G is None:
G = 1
foo()
I get an error:
UnboundLocalError: local variable 'G' referenced before assignment
What am I doing wrong?
You need the global statement:
def foo():
global G
if G is None:
G = 1
In Python, variables that you assign to become local variables by default. You need to use global to declare them as global variables. On the other hand, variables that you refer to but do not assign to do not automatically become local variables. These variables refer to the closest variable in an enclosing scope.
Python 3.x introduces the nonlocal statement which is analogous to global, but binds the variable to its nearest enclosing scope. For example:
def foo():
x = 5
def bar():
nonlocal x
x = x * 2
bar()
return x
This function returns 10 when called.
You need to declare G as global, but as for why: whenever you refer to a variable inside a function, if you set the variable anywhere in that function, Python assumes that it's a local variable. So if a local variable by that name doesn't exist at that point in the code, you'll get the UnboundLocalError. If you actually meant to refer to a global variable, as in your question, you need the global keyword to tell Python that's what you meant.
If you don't assign to the variable anywhere in the function, but only access its value, Python will use the global variable by that name if one exists. So you could do:
G = None
def foo():
if G is None:
print G
foo()
This code prints None and does not throw the UnboundLocalError.
You still have to declare G as global, from within that function:
G = None
def foo():
global G
if G is None:
G = 1
foo()
print G
which simply outputs
1
Define G as global in the function like this:
#!/usr/bin/python
G = None;
def foo():
global G
if G is None:
G = 1;
print G;
foo();
The above python prints 1.
Using global variables like this is bad practice because: http://c2.com/cgi/wiki?GlobalVariablesAreBad

Variable scope in nested functions

Could someone explain why the following program fails:
def g(f):
for _ in range(10):
f()
def main():
x = 10
def f():
print x
x = x + 1
g(f)
if __name__ == '__main__':
main()
with the message:
Traceback (most recent call last):
File "a.py", line 13, in <module>
main()
File "a.py", line 10, in main
g(f)
File "a.py", line 3, in g
f()
File "a.py", line 8, in f
print x
UnboundLocalError: local variable 'x' referenced before assignment
But if I simply change the variable x to an array, it works:
def g(f):
for _ in range(10):
f()
def main():
x = [10]
def f():
print x[0]
x[0] = x[0] + 1
g(f)
if __name__ == '__main__':
main()
with the output
10
11
12
13
14
15
16
17
18
19
The reason I am confused is, if from f() it can't access x, why it becomes accessible if x is an array?
Thanks.
But this answer says the problem is with assigning to x. If that's it,
then printing it should work just fine, shouldn't it?
You have to understand the order in which things happen. Before your python code is even compiled and executed, something called a parser reads through the python code and checks the syntax. Another thing the parser does is mark variables as being local. When the parser sees an assignment in the code in a local scope, the variable on the lefthand side of the assignment is marked as local. At that point, nothing has even been compiled yet--let alone executed, and therefore no assignment takes place; the variable is merely marked as a local variable.
After the parser is finished, the code is compiled and executed. When execution reaches the print statement:
def main():
x = 10 #<---x in enclosing scope
def f():
print x #<-----
x = x + 1 #<-- x marked as local variable inside the function f()
the print statement looks like it should go ahead and print the x in the enclosing scope (the 'E' in the LEGB lookup process). However, because the parser previously marked x as a local variable inside f(), python does not proceed past the local scope (the 'L' in the LEGB lookup process) to lookup x. Because x has not been assigned to in the local scope at the time 'print x' executes, python spits out an error.
Note that even if the code where an assignment occurs will NEVER execute, the parser still marks the variable on the left of an assignment as a local variable. The parser has no idea about how things will execute, so it blindly searches for syntax errors and local variables throughout your file--even in code that can never execute. Here are some examples of that:
def dostuff ():
x = 10
def f():
print x
if False: #The body of the if will never execute...
a b c #...yet the parser finds a syntax error here
return f
f = dostuff()
f()
--output:--
File "1.py", line 8
a b c
^
SyntaxError: invalid syntax
The parser does the same thing when marking local variables:
def dostuff ():
x = 10
def f():
print x
if False: #The body of the if will never execute...
x = 0 #..yet the parser marks x as a local variable
return f
f = dostuff()
f()
Now look what happens when you execute that last program:
Traceback (most recent call last):
File "1.py", line 11, in <module>
f()
File "1.py", line 4, in f
print x
UnboundLocalError: local variable 'x' referenced before assignment
When the statement 'print x' executes, because the parser marked x as a local variable the lookup for x stops at the local scope.
That 'feature' is not unique to python--it happens in other languages too.
As for the array example, when you write:
x[0] = x[0] + 1
that tells python to go lookup up an array named x and assign something to its first element. Because there is no assignment to anything named x in the local scope, the parser does not mark x as a local variable.
The reason is in first example you used an assignment operation, x = x + 1, so when the functions was defined python thought that x is local variable. But when you actually called the function python failed to find any value for the x on the RHS locally, So raised an Error.
In your second example instead of assignment you simply changed a mutable object, so python will never raise any objection and will fetch x[0]'s value from the enclosing scope(actually it looks for it firstly in the enclosing scope, then global scope and finally in the builtins, but stops as soon as it was found).
In python 3x you can handle this using the nonlocal keyword and in py2x you can either pass the value to the inner function or use a function attribute.
Using function attribute:
def main():
main.x = 1
def f():
main.x = main.x + 1
print main.x
return f
main()() #prints 2
Passing the value explicitly:
def main():
x = 1
def f(x):
x = x + 1
print x
return x
x = f(x) #pass x and store the returned value back to x
main() #prints 2
Using nonlocal in py3x:
def main():
x = 1
def f():
nonlocal x
x = x + 1
print (x)
return f
main()() #prints 2
The problem is that the variable x is picked up by closure. When you try to assign to a variable that is picked up from the closure, python will complain unless you use the global or nonlocal1 keywords. In the case where you are using a list, you're not assigning to the name -- You can modify an object which got picked up in the closure, but you can't assign to it.
Basically, the error occurs at the print x line because when python parses the function, It sees that x is assigned to so it assumes x must be a local variable. When you get to the line print x, python tries to look up a local x but it isn't there. This can be seen by using dis.dis to inspect the bytecode. Here, python uses the LOAD_FAST instruction which is used for local variables rather than the LOAD_GLOBAL instruction which is used for non-local variables.
Normally, this would cause a NameError, but python tries to be a little more helpful by looking for x in func_closure or func_globals 2. If it finds x in one of those, it raises an UnboundLocalError instead to give you a better idea about what is happening -- You have a local variable which couldn't be found (isn't "bound").
1python3.x only
2python2.x -- On python3.x, those attributes have changed to __closure__ and __globals__ respectively
The problem is in the line
x = x + 1
This is the first time x being assigned in function f(), telling the compiler that x is a local name. It conflicts with the previous line print x, which can't find any previous assignment of the local x.
That's where your error UnboundLocalError: local variable 'x' referenced before assignment comes from.
Note that the error happens when compiler tries to figure out which object the x in print x refers to. So the print x doesn't executes.
Change it to
x[0] = x[0] + 1
No new name is added. So the compiler knows you are referring to the array outside f().

Python global/local variables

Why does this code work:
var = 0
def func(num):
print num
var = 1
if num != 0:
func(num-1)
func(10)
but this one gives a "local variable 'var' referenced before assignment" error:
var = 0
def func(num):
print num
var = var
if num != 0:
func(num-1)
func(10)
Because in the first code, you have created a local variable var and used its value, whereas in the 2nd code, you are using the local variable var, without defining it.
So, if you want to make your 2nd function work, you need to declare : -
global var
in the function before using var.
def func(num):
print num
var = 1 <-- # You create a local variable
if num != 0:
func(num-1)
Whereas in this code:
def func(num):
print num
var = var <--- # You are using the local variable on RHS without defining it
if num != 0:
func(num-1)
UPDATE: -
However, as per #Tim's comment, you should not use a global variable inside your functions. Rather deifine your variable before using it, to use it in local scope. Generally, you should try to limit the scope of your variables to local, and even in local namespace limit the scope of local variables, because that way your code will be easier to understand.
The more you increase the scope of your variables, the more are the chances of getting it used by the outside source, where it is not needed to be used.
If you have var = ... anywhere in a function, the name "var" will be treated as a local variable for the entire function, regardless of where that assignment occurs. This means that all occurrences of var in your function will be resolved in the local scope, so the right hand side of var = var results in the referenced before assignment error because var has not yet been initialized in the function's scope.
You can read a global without declaring it global. But to write a global, you need to declare it global.
In your second piece of code you have created a local variable in RHS and without defining it, you are assigning it to the LHS variable var which is global (a variable defined outside the function is considered global explicitly).
If your intention is to create a local variable inside the function and assign it to the value of the global variable, this will do the trick:
var = 0
def func(num):
print num
func.var = var # func.var is referring the local
# variable var inside the function func
if num != 0:
func(num-1)
func(10)
def runscan(self):
p = os.popen('LD_PRELOAD=/usr/libv4l/v4l1compat.so zbarcam
/dev/video0','r')
def input(self):
self.entryc.insert(END, code)
how about this?
i want use local 'code' to the next function to insert the result of barcode to my Tkinter entryBox..
Thanks
Each function block is a local scope. If you want to assign to global variables, you need to do so explicitly:
var = 0
def func(num):
print num
global var
var = 1
if num != 0:
func(num-1)
print var # 0
func(2)
print var # 1

Categories