When is symbol table created in Python - python

def square():
print(a)
a = 10
b = a*a
return b
a = 2
print(square())
UnboundLocalError: local variable 'a' referenced before assignment
def square():
print(a)
#a = 10
b = a*a
return b
a = 2
print(square())
I just want to make sure why the second scenario is right while the first is wrong.
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.
While interpreter is going through the definition of the function it creates a symbol table. It first thinks 'a' as global(due to print(a)) and later creates 'a' in local symbol table(Due to assignment).
So while actually executing the function, 'a' is a local which has no binding. Hence the error.
Is my reasoning right about symbol tables??
Update: Adding global after assignment:
def square():
print(a)
a = 10
b = a*a
global a
return b
a = 2
print(square())
print(a)
Does global a statement remove name 'a' from the local symbol table of square function?

It's a scope issue. Check this answer : https://stackoverflow.com/a/293097/1741450
Variables in scopes other than the local function's variables can be accessed, but can't be rebound to new parameters without further syntax. Instead, assignment will create a new local variable instead of affecting the variable in the parent scope.
The first example is wrong since a cannot be modified.

Related

Why one variable is accessed globally and other is not in Python?

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

Use a dictionary to refer to a global variable inside a function (Python)

I'm using a dictionary to refer to a global variable in Python inside a function. I want to use the function to update the global variable. (In reality my global variables are more complicated than this.)
global_variable_A=5
global_variable_B=1
dictionary={'A':global_variable_A,'B':global_variable_B}
def f(x,glob):
global global_variable_A
dictionary[glob]+=x
f(2,'A')
print(global_variable_A)
This returns 5 rather than 7. I understand why, is there anyway to let Python know that I mean the variable dictionary[glob] to refer to the global rather than the local variable, while still referring to the variable through a dictionary?
Thanks for your time, and apologies if I've missed something completely obvious.
When you assign a value to a name name = 5, you're creating a reference to 5 that you can use the identifier name to access. Normally, if you then have some code with a narrower scope, you can either use that reference
def f():
print(name)
or create a local reference using the same identifier, potentially to an unrelated value
def g():
name = 100
print(name)
The global keyword allows you to instead manipulate the identifier as if you weren;t in the more narrow scope, allowing you to reassign the global name to a different reference:
def h():
global name
name = 100
h()
print(name) # 100
However, when you use a reference to create another reference, there isn't any relation ship between those two references. So
name = 5
l = [name]
leaves us with two references to the value 5: one from the identifier name, and one from the first position of l. Crucially, those two references are not related; we can change one without changing the other.
name = 6
print(l) # [5]
One way to accomplish what you want is to use a boxed type. That means that you create an object that points to another object. All of your references can then point to the first object (the box) and you can freely change what it points to (the thing "inside" the box), by only updating a single reference:
class Box:
def __init__(self, value):
self.value = value
box = Box(5)
l = [box]
box.value = 10
print(l[0].value) # 10
If you think you need to do this, there's a design flaw in your code. I really wouldn't recommend doing this.
global_a = 5
def add_to_global(key, value):
globals()[key] += value
add_to_global("global_a", 2)
print(global_a)
Output:
7

How to fix two object same name 'local variable referenced before assignment' Error in Python

I defined one function, and in the function or another function I assign the same-name value by call the same-name function. I get this error
UnboundLocalError: local variable 'demo01' referenced before assignment
The error occurs here
def demo01():
return 5
def demo02():
demo01=demo01()
demo02()
UnboundLocalError: local variable 'demo01' referenced before assignment
But these snippets are fine
def demo01():
return 5
def demo02():
demo01=demo01()
demo01 = demo01()
def demo01():
return 5
def demo02():
demo02=demo01()
demo02()
When there is an existing variable, creating a new one under that name will overwrite the existing variable, for example:
print = 2 # change the print() function to a variable named 2
print('string')
will give
TypeError: 'int' object is not callable
going back to your code:
demo01 = lambda: 5 # this is more of an advanced keyword, feel free to do it your way
def demo02():
demo01 = demo01() # "demo01 = [val]" tells python to assign a value to demo01, and you cannot assign a variable to the same variable:
variable = variable
obviously not possible; gives
NameError: name 'variable' is not defined
when used in a global state and UnboundLocalError when used in a local (class or function).
Your variable names and other variables, if any, used during assignment MUST NOT BE OR REFERENCE THE VARIABLE YOU ARE CURRENTLY ASSIGNING.
If you really need to use the same variable:
variable_ = variable()
variable = variable_

Why does the scope work like this?

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

Declaring global variables

I have 2 question regarding global variables:
Why can't I declare a list as a global variable as so: global list_ex = []?
I have already defined a global variable that I am trying to use in a function, but can't:
global column
def fx_foo(cols):
common = set(cols).intersection(set(column)) #Error Here!!
When I try to access column inside the function, I get an error:
NameError: global name 'column' is not defined
You are not using global correctly. You don't need to use it at all.
You need to actually set a global column variable, there is none right now. global does not make the variable available. Just create a global column first:
column = []
then refer to it in your function. That is what the NameError exception is trying to tell you; Python cannot find the global column variable, you didn't assign anything to the name so it doesn't exist.
You only need to use global if you want to assign to a global column in your function:
def somefunction():
global column
column = [1, 2, 3]
Here the global keyword is needed to distinguish column from a local variable in the function.
Compare:
>>> foo = 1
>>> def set_foo():
... foo = 2
...
>>> set_foo()
>>> foo
1
to
>>> foo = 1
>>> def set_foo():
... global foo
... foo = 2
...
>>> set_foo()
>>> foo
2
The first form only set a local variable, the second form set the global variable instead.
The keyword global means you are explicitly using a variable declared outside the scope of a function.
Your variable must be declared normally:
column = []
and declared global in the function that uses it
def fx_foo(cols):
global column
common = set(cols).intersection(set(column))
It is used to allow python to distinguish between new local variables and reused global variables.
this will work :
column =[]
def fx_foo(cols):
global column
common = set(cols).intersection(set(column))
but this will work even without global as column will be considered as nonlocal here
column =[]
def fx_foo(cols):
common = set(cols).intersection(set(column))
I think It is more interesting to assign data to column if You want to display global feature (as You can use column from nonlocals without global declaration if you don't assign anything to it)
column =[]
def fx_foo(cols):
global column
column = set(cols).intersection(set(column))
or
def fx_foo(cols):
column =[]
global column
column = set(cols).intersection(set(column))
Global variables need to be declared global inside of the function not on the outside. A variable declared outside a function is by default global.
Functions don't need to declare a variable as global if they only need to access it. The global declaration is just if you need to modify the global variable.
Therefore, in your case, I suggest that you look into what column actually is when you're passing it to the function, because I think the problem may have something to do with that.

Categories

Resources