If this works
x=5
def main():
for globe in locals():
globals().update(locals()[globe])
print x
main()
then why doesn't this?
x=5
def main():
for globe in locals():
globals().update(locals()[globe])
x+=1
print x
main()
The error in the latter statement claims that x is referenced before assignment, however it works in the first example...
In python when you assign a variable the declaration happens automatically.
So when you assign a value to x inside the function, python think that is a new local variable, shadowing the global x.
if you want to assign a value to the global x you can do this:
x=5
def main():
global x
x += 1
print x
main()
You cannot assign a global variable in python without explicitly doing so. By writing x+=1 you are assigning a value to x and are implicitly declaring x as a local variable. But it is not defined and therefore you get an error.
The loop has no actual effect, as the locals dictionary is empty.
If you want to use global variables in Python (which you shouldn't, but that's another matter), you should use the global keyword.
suffixing 1 and 2 your two functions, you can find local names (syntax slightly different in python 2) :
In [7]: main1.__code__.co_varnames
Out[7]: ('globe',)
In [8]: main2.__code__.co_varnames
Out[8]: ('globe', 'x')
So x have different status. In the second case the local x mask the global one, so x=x+1 cause the error, because not yet defined.
From docs :
If a name is bound in a block, it is a local variable of that block, unless declared as nonlocal or global.(...).If a variable is used in a code block but not defined there, it is a free variable.
Related
I am trying to implement a simple BFS code.
Code:
def initalizeVertex():
n = int(input('Enter the no. of vertices.\n'))
for vertex in range(n):
Vertex.append(vertex)
node_adjacency[vertex] = []
def initalizeUndirectedEdges():
n = int(input("Enter the no. of edges.\n"))
print("Enter the space seperated edges.")
for i in range(n):
a,b = map(int,input().split())
node_adjacency[a].append(b)
node_adjacency[b].append(a)
def bfs():
current_level = 1
print(Vertex)
while(current_level_nodes):
for current_node in current_level_nodes:
for child_node in node_adjacency[current_node]:
if node_parent.get(child_node) == None:
node_parent[child_node] = current_node
next_level_nodes.append(child_node)
node_level[child_node] = current_level
current_level_nodes = [node for node in next_level_nodes]
next_level_nodes = []
current_level += 1
print(node_level)
print(node_parent)
def printData():
print(Vertex)
print(node_adjacency)
print(node_parent)
print(node_level)
print(current_level_nodes)
print(next_level_nodes)
if __name__ == "__main__":
root = 0
Vertex = []
node_adjacency = {}
node_parent = {root:None}
node_level = {root:0}
current_level_nodes = [root]
next_level_nodes = []
initalizeVertex()
initalizeUndirectedEdges()
printData()
bfs()
The codes output is :
This code gives me the error:
in bfs
while(current_level_nodes):
UnboundLocalError: local variable 'current_level_nodes' referenced before assignment
Clearly, the bfs() function cannot access the list current_level_nodes. I can solve this by passing the list current_level_nodes to the bfs function.
But, my question is why do I have to pass this list to bfs() function when I haven't passed the list Vertex to initalizeVertex() function and the function could still access and modify it?
Also I haven't passed any parameter to printData function but that function prints all the lists and dictionaries without giving any error.
Python will try to look for the variable at local scope 1st. If they're not found locally, Python will look for them globally.
for example -
In your printData() function -
Python will try to look for the variables locally, but no variables are defined in this method. So, Python will go at the global scope. It'll pick the values and will print them.
def printData():
print(Vertex)
print(node_adjacency)
print(node_parent)
print(node_level)
print(current_level_nodes)
print(next_level_nodes)
In another function bfs() -
Python will see current_level_nodes inside a while, and Python will try to find where the variable is defined. Here things will get tricky as you've assigned the same variable in the function later. This will confuse Python, and it'll throw the error.
line 1. - while(current_level_nodes):
line 2. - current_level_nodes = [node for node in next_level_nodes]
That's why you need to explicitly tell Python that you want to use global variable -
global current_level_nodes
The issue here involves the python compiler having compiled the rest of your function and it realizing that it must bind your variables within the function. This requires the global keyword as previously mentioned.
What is confusing here is that the error is shown on the first usage of the variable and not where the actual problem is. Here is an example:
access_only.py:
global_list = [1,2,3]
def f():
for x in global_list:
print(x)
f()
Here, we run it:
$python access_only.py
1
2
3
Now we change the function slightly to include an assignment to the list, as occurs in your code.
access_and_change.py:
global_list = [1,2,3]
def f():
for x in global_list:
global_list = [1, 2, 3]
print(x)
f()
Here we run it, but get an error on the for line even though the problem actually occurs on the global_list = line:
$python access_and_change.py
Traceback (most recent call last):
File "access_and_change.py", line 9, in <module>
f()
File "access_and_change.py", line 4, in f
for x in global_list:
UnboundLocalError: local variable 'global_list' referenced before assignment
The end result is the same, because you are assigning values to the lists, the global keyword must be used. If you eliminate the assignment within your function, the error goes away completely.
Thinking about this, I noticed that the error message says local variable referenced before assignment. I think the issue here is that because the function contains an assignment, the variable is considered to be local. Whereas if you just reference a global variable it is not considered a local variable. So, the assignment causes the variable to be considered local and the first time you reference it, python throws this error instead of searching the global/parent namespace because the compiler has already determined that it must be a local variable.
You have to define current_level_nodes is global in your bfs function this way :
def bfs():
current_level = 1
global current_level_nodes
...
Edit :
To access a global variable inside a function there is no need to use global keyword.
But if we need to assign a new value to a global variable or change it then we can do that by declaring the variable as global.
Because you used
next_level_nodes.append(child_node)
and
current_level_nodes = [node for node in next_level_nodes]
You need to use global;
If you didn't modify current_level_nodes in your function there was no need to use global
I have a question on a fairly simple task in python, however, I didn't manage to find a solution. I would like to assign a new value to an already existing variable in python. However, the changes I am doing to the variable within the function don't stick to the variable.
Here is a simplified example of my problem:
y = 1
x = None
def test(var):
var = y
return var
test(x)
print(x)
The print simply returns none. So the changes I have done to the variable within the function are non permanent.
How can I make the changes on the input-variable of the function permanent?
Thanks in advance!
Variables in Python are just names which refer to objects. In an expression, the name is a stand-in for the actual object. Saying test(x) means "pass the object referred to by x into test". It does not mean "pass the symbol x into test".
In addition, re-assigning a name only changes what object that name refers to. It affects neither the object nor any of its aliases.
In short, the name var you modify inside test has no relation to x at all.
The preferred way to have a function change something is by reassigning the result:
x = 2
def change(var):
return var * 2
x = change(x) # x now refers to 4 instead of 2
print(x)
If you want to change a name outside a function, you can use the nonlocal and global keywords:
x = 2
def change_x():
global x
x = x * 2
change_x() # x now refers to 4 instead of 2
print(x)
While this can make some trivial problems easy to solve, it is generally a bad idea for larger programs. Using global variables means one can no longer use the function in isolation; results may depend on how often and in what order such a function is called.
If you have some self-contained group of values and means to modify them, a class can be used to describe this:
class XY:
def __init__(self, x, y):
self.x, self.y = x, y
def swap(self):
self.x, self.y = self.y, self.x
my_values = XY(None, 1)
print(my_values.x, my_values.y)
my_values.swap()
print(my_values.x, my_values.y)
In contrast to global variables, you can create as many isolated instances of classes as needed. Each instance can be worked on in isolation, without affecting the others.
You can also use mutable values to make changes visible to the outside. Instead of changing the name, you modify the value.
x = [2] # x is a mutable list, containing the value 2 at the moment
def change(var):
var[0] = 4 # change leading element of argument
change(x) # x now contains 4 instead of 2
print(x)
This is an example of passing variables to functions by value. By default, when you pass a variable to a function in Python, it is passed by value.
What it means is, that a new variable with a new scope is created with the same value of x. So, any change that happens to the new x is not reflected to the x outside the function's scope.
If you want to get the value from the function back, you can use the return statement (as you have used). return returns the value back from the function. However, in your example there is no variable to receive it. Hence, it is lost.
You would have to call the function as x = test(x). This ensures that x receives the value back from the function.
This question is a result of a student of mine asking a question about the following code, and I'm honestly completely stumped. Any help would be appreciated.
When I run this code:
#test 2
a = 1
def func2(x):
x = x + a
return(x)
print(func2(3))
it works perfectly fine. It is able to take the globally-scoped variable a and use its value to perform the calculation and return the value 4.
However, if I change it to this:
# test 3
a = 1
def func3(x):
a = x + a
return(x)
print(func3(3))
I then get an error:
local variable 'a' referenced before assignment
Why do I get this error only when I want to update the value of a within the function to a new value based on its original value? What am I not understanding? I feel like this second piece of code should work fine.
Thanks in advance for any help and insight.
a = 1
def func3(x):
global a
a = x + a
return(x)
print(func3(3))
Now it should work.
When you put the statement a=x+a inside the function, it creates a new local variable a and tries to reference its value(which clearly hasnt been defined before). Thus you have to use global a before altering the value of a global variable so that it knows which value to refer to.
EDIT:
The execution of a function introduces a new symbol table used for the
local variables of the function. More precisely, all variable
assignments in a function store the value in the local symbol table;
whereas variable references first look in the local symbol table, then
in the local symbol tables of enclosing functions, then in the global
symbol table, and finally in the table of built-in names. Thus, global
variables cannot be directly assigned a value within a function
(unless named in a global statement), although they may be referenced.
def func3(x):
a = x + a
return(x)
On the right hand right side of a = x + a (So, x + a), 'x' is passed as variable, where 'a' is not passed as a variable thus an error.
Without using globals:
a = 1
def func3(x, a=2):
a = x + a
return(x)
func3(3)
Returns: 5
This question already has answers here:
Assigning to variable from parent function: "Local variable referenced before assignment" [duplicate]
(5 answers)
Closed 9 years ago.
I've tried looking at a few different examples, but I'm not really sure why this isn't working. Say I've some code like this:
def loadVariable():
global count
count = 0
def loadDictionary():
location = 'some location'
global myDict
myDict = pickle.load(open(location, 'rb'))
def main():
loadVariable()
loadDictionary()
for item in myDict:
if item.startswith("rt"):
count += 1
item = item[3:]
if __name__ == '__main__':
main()
To my eyes, the if statement is executed which starts the main() method. Then, the variable which is global is loaded, the dictionary is loaded and the for loop is executed.
However, when I run the code I am told that the local variable count is referenced before its assignment. Why is that happening?
Edit (Explaining some of the things I've written in comments):
This doesn't work (although I think that's because global is used wrong here):
global count
def loadVariables()
count = 0
def main():
loadVariables()
rest of code etc
This doesn't work either:
def loadVariables()
count = 0
def main():
global count
loadVariables()
rest of code etc
The only way thus far I've gotten it to work is using the link provided above, which is to treat the count as a list, like so:
def loadVariables():
global count
count = [0]
def main():
loadVariables():
rest of code etc
count[0] += 1
global means that within the function containing the global declaration, the name in the global declaration refers to a global variable. It does not mean "this thing is a global variable; treat it as global everywhere." In main, the names count and myDict refer to local variables, because main does not declare that it wants to use the globals.
The issue is that you're not declaring count as a global variable in the main function, so when the compiler sees that you're (eventually) assigning to it, it assumes that it's a local variable. Since it's value is read before it's assigned, you get an exception.
So, the most basic fix is just to add global count at the top of main(), but I think avoiding globals would be a better option. Why not have loadVariable and loadDictionary return their results, rather than assigning them to globals? If in main() you did count = loadVariable(), count would be a local variable, and you'd have no problems later trying to reassign it.
Here's a simple example of how global works
global_var = 0
def updater():
global global_var
global_var += 1
def stuff(x):
updater()
return global_var + x
if __name__ == '__main__':
stuff(2) # returns 3
This question already has answers here:
nonlocal keyword in Python 2.x
(10 answers)
Closed 6 months ago.
Have a look at this code:
def closure():
value = False
def method_1():
value = True
def method_2():
print 'value is:', value
method_1()
method_2()
closure()
I would expect it to print 'Value is: True' but it doesn't. Why is this and what is the solution?
This happens because method_1 gets its own local scope where it can declare variables. Python sees value = True and thinks you're creating a new variable named value, local to method_1.
The reason Python does this is to avoid polluting the outer scope's locals with variables from an inner function. (You wouldn't want assignments in regular, module-level functions to result in global variables being created!)
If you don't assign to value, then Python searches the outer scopes looking for the variable (so reading the variable works as expected, as demonstrated by your method_2).
One way to get around this is by using a mutable object instead of assigment:
result = { 'value': False }
def method_1():
result['value'] = True
In Python 3, the nonlocal statement (see also docs) was added for exactly this scenario:
def method_1():
nonlocal value
value = True # Works as expected -- assigns to `value` from outer scope
In method_1, Python assumes (quite sensibly!) that value is a local variable. Whenever you assign to a variable name inside a function, it is assumed that that variable name is a new local variable. If you want it to be global, then you have to declare it as global, and if you want it to be "nonlocal", in Python 3, you can declare it nonlocal, but in Python 2, you have to do something uglier: store the value in a container. That avoids having to reassign the variable name, and so avoids the scoping ambiguity.
def method_1_global():
global value
value = True
def method_1_nonlocal_P3():
nonlocal value
value = True
value = [False]
def method_1_nonlocal_P2():
value[0] = True
When you assign to a variable, it assumes the variable is of local scope. So the value in method_1 is not the value in closure.
If you want this to work on Python 3, add a line to method_1: nonlocal value.
On Python 2,
def closure():
value = [False]
def method_1():
value[0] = True
def method_2():
print 'value is:', value
method_1()
method_2()
closure()
is one possible work-around.
This is because you are not actually modified the closed-over variable - you are masking it with a new one that has the same name. In python 2.x there is no easy way around this, which is why the nonlocal keyword was added in python 3.
This can be worked around, however, using mutable types like list and dictionary. There is a good example in this answer.
To avoid this, you can use a list.
value = [False]
def method_1():
value[0] = True
What Python does now is searching in higher levels of the scope as value is not available in the local variables. As value is a list and Python refernces it as a global variable relative to *method_1*, you can treat value as it is, a list.