values arguments in pdg - python

I have created a simple function on python and make some tests with the debugger to see how it works.
I wanted to print the value of a variable (raiz):
(dbg) p raiz
But it says that it's not defined. Why is this? Here is my code:
import math
def funcion(sen):
raiz = math.sqrt(sen)
return "the sqrt of " + repr(sen) + 'is ' + repr(raiz)
print (funcion(38))
import pudb;
pudb.set_trace()

Your variable is declared inside the function, not outside, so the code can't see it.
If you need to set value for outside variable, mark it with global keyword (which is a bad practice):
import math
raiz = None
def funcion(sen):
nonlocal raiz
raiz = math.sqrt(sen)
return "the sqrt of " + repr(sen) + 'is ' + repr(raiz)
funcion(38)
print (raiz)
Also you can use nonlocal keyword, but for this case it would fail with:
SyntaxError: no binding for nonlocal 'raiz' found
You can find a tutorial about local, nonlocal and global keywords here.

This is to do with Scope.
The place that variables are defined is important, and defining them in certain places will cause them to disappear elsewhere. Here is an example:
# PLEASE NOTE: This code will fail.
a = "Hello"
def my_func():
b = "Hello" # Declare a variable 'b', but only in this scope, in other words this function.
my_func()
print(a) # Works, since a is in the same scope.
print(b) # Fails, since b was defined inside a different scope, and discarded later.
Since, in your example, raiz was declared inside the function funcion, Python discarded it after funcion was called. If you put the line:
raiz = 0
before your function definition, the function would simply update the variable, so there would be no issue, since the scope would be the same as where the print statement occurs.
tl;dr: The variable raiz was declared only in funcion, so it was discarded afterwards.

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

Python : define a constant used by several functions [duplicate]

This question already has answers here:
Why isn't the 'global' keyword needed to access a global variable?
(11 answers)
Closed 2 years ago.
In Python, I was wondering something about defining a variable in a module which can be used by all functions in that module.
I know that if I define a variable outside function, I can access that variable in a function by introducing global
e.g : inside a module name gas.py
R = 8.314 # univsersal gas constant
def pressure(T, V, n):
global R
P = n*R*T/V
return P
def temperature(P,V,n):
global R
T = P*V/(R*n)
return T
But as you can see, I have to write global R inside each function.
Is there a way that I can access R without writing global R inside each function ?
For example :
R = 8.314 # univsersal gas constant
def pressure(T, V, n):
P = n*R*T/V
return P
def temperature(P,V,n):
T = P*V/(R*n)
return T
Thank you
In python, you can read the value of a global variable without declaring it global.
You only need the global declaration when you want to change the value of a global variable. So in your case, you can just delete the all global declarations.
No, in Python a global variables works in a different way compared to other programming languages.
If you want to access a variable contained outside a function, you can just call it like a regular variable, for example:
word = "hello"
def function():
print(word)
function()
Output: hello
If you want to edit a variable locally, but not globally, you have to reassign the variable inside the function, for example:
word = "hello"
def function():
word = "world"
print(word)
print(word)
Output: hello, because we reassigned the variable only inside the function, so the global value of the variable word is still hello
But, if we want to edit a global variable (such as in case 1) in a non-global scope (local scope) and we want to manipulate the value of that variable inside a function, we have to use the global declaration, for example:
word = "hello"
def function():
global word
word = "world"
function()
print(word)
Now the Output will be world.

Is there a way to use variable from outer function in the inner function body, in Python?

The pattern of the code that I try to build is:
def OuterFunction():
theVariable = 0
def InnerFunction():
# use theVariable from OuterFunction somehow
# the solution is not to use InnerFunction(theVariable) due to the reasons given in the text
InnerFunction()
My hopes were there is some keyword (like global) or a way to tell the interpreter to use theVariable from the outer method's scope.
Why I need it this way:
A Python script that we run before has to become module now (the collection of methods).
OuterFunction did not exist before and InnerFunction is just an example for a big number of very complex methods that bear an already existing logic.
theVariable is just an example for multiple global variables that were used in the script in all of methods that InnerFunction represents.
I do not want to change signatures of all methods, that are, by the way, nested.
EDIT:
Here is the code that crashes for me in every interpreter, with "local variable 'theVariable' referenced before assignment" error (so I could not just reference the variable):
def OuterFunction():
theVariable = 5
def InnerFunction():
theVariable = theVariable + 1
print(theVariable)
InnerFunction()
OuterFunction()
EDIT2:
Seems trying to change the variable leads to the exception, which gives a wrong description.
If InnerFunction() is changed to contain only print(theVariable) statement it works.
You can just reference the variable directly, as follows;
def outer():
x = 1
def inner():
print(x + 2)
inner()
outer()
Prints: 3
You could simply reference the 'theVariable' inside the nested InnerFunction, if you don't want to pass it's value as a parameter:
def OuterFunction():
# Declare the variable
theVariable = 42
def InnerFunction():
# Just reference the 'theVariable', using it, manipulating it, etc...
print(theVariable)
# Call the InnerFunction inside the OuterFunction
InnerFunction()
# Call the OuterFunction on Main
OuterFunction()
# It will print '42' as result
Would this be okay with you:
def OuterFunction():
theVariable = 0
def InnerFunction(value):
return value + 1
theVariable = InnerFunction(theVariable)
return theVariable
This way you don't have to mess with scopes and your functions are pure.

Merging global and local scopes

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.

How to create variables local to with statement?

I would like to create temporary variables visible in a limited scope.
It seems likely to me that you can do this with a "with" statement, and I would think there is a construct that makes it easy to do, but I cannot seem to find it.
I would like something like the following (but it does not work this way of course):
pronunciation = "E_0 g z #_1 m p l"
# ...
with pronunciation.split() as phonemes:
if len(phonemes) > 2 or phonemes[0].startswith('E'):
condition = 1
elif len(phonemes) < 3 and phonemes[-1] == '9r':
condition = 2
So is there a simple way to make this work, using built-ins?
Thanks!
Python creates local variables with function scope (once a name is used it stays alive until the end of the function).
If you really want to limit scope then "del <var>" when you want it explicitly discarded, or create separate function to act as a container for a more limited scope.
You can create a method
def process_pronunciation(pronunciation):
phonemes = pronunciation.split()
if len(phonemes) > 2 or phonemes[0].startswith('E'):
condition = 1
elif len(phonemes) < 3 and phonemes[-1] == '9r':
condition = 2
return condition
When you call the method, the local variable phonemes won't be available in the global namespace.
pronunciation = "E_0 g z #_1 m p l"
condition = process_phonemes(pronunciation)
You could do it with with, but I don't think it's worth the trouble. Basically (in a python function) you have two scopes - global or local, that's it. If you want a symbol to have a lifespan shorter than the function you'll have to delete it afterwards using del. You could define your own context manager to make this happen:
class TempVar:
def __init__(self, loc, name, val):
self.loc = loc
self.name = name
self.val
def __enter__(self):
if self.name in self.loc:
self.old = self.loc[self.name]
self.loc[self.name] = self.val
def __exit__(self, *exc):
if hasattr(self, "old"):
self.loc[self.name] = self.old
else:
del self.loc[self.name]
then you can use it to get a temporary variable:
with TempVar(locals(), "tempVar", 42):
print(tempVar)
The working is that it modifies the dict containing local variables (which is supplied to the constructor via locals()) on entry and restoring it when leaving. Please note that this relies on that modifying the result returned by locals() actually modifies the local namespace - the specification does NOT guarantee this behaviour.
Another (and safer) alternative that was pointed out is that you could define a separate function which would have it's own scope. Remember it's perfectly legal to nest functions. For example:
def outer():
def inner(tempVar):
# here tempVar is in scope
print(tempVar)
inner(tempVar = 42)
# here tempVar is out of scope
with statement does not have its own scope , it uses the surrounding scope (like if the with statement is directly inside the script , and not within any function, it uses global namespace , if the with statement is used inside a function, it uses the function's namespace(scope)).
If you want the statements inside a with block to run in its own local scope, one possible way would be to move the logic to a function , that way the logic would be running in its own scope (and not the surrounding scope of with.
Example -
def function_for_with(f):
#Do something.
with pronunciation.split() as phonemes:
function_for_with(phonemes)
Please note, the above will not stop phonemes from being defined in the surrounding scope.
If you want that as well (move the phonemes into its own scope), you can move the complete with statement inside a function. Example -
def function_with(pronunciation):
with pronunciation.split() as phonemes:
#do stuff
pronunciation = "E_0 g z #_1 m p l"
function_with(pronunciation)
Expanding on #skyking's answer, here's an even more magical implementation of the same idea that reads almost exactly like you wrote. Introducing: the with var statement!1
class var:
def __init__(self, value):
import inspect
self.scope = inspect.currentframe().f_back.f_locals
self.old_vars = set(self.scope.keys())
self.value = value
def __enter__(self):
return self.value
def __exit__(self, type, value, traceback):
for name in set(self.scope.keys()) - self.old_vars:
del self.scope[name]
### Usage:
line = 'a b c'
with var (line.split()) as words:
# Prints "['a', 'b', 'c']"
print(words)
# Causes a NameError
print(words)
It does all the nasty extracting of local variables and names for you! How swell. If you space it quirkily like I did and hide the definition in a from boring_stuff import * statement, you can even pretend var is a keyword to all of your confused co-workers.
[1] If you actually use this, the ghost of a dead parrot will probably haunt you forever. The other answers provide much saner solutions; this one is more of a joke.

Categories

Resources