Confusing change of scope - what's going on? - python

def Test(value):
def innerFunc():
print value
innerFunc()
def TestWithAssignment(value):
def innerFunc():
print value
value = "Changed value"
innerFunc()
Test("Hello 1")
# Prints "Hello 1"
TestWithAssignment("Hello 2")
# Throws UnboundLocalError: local variable 'value' referenced before assignment
# on the "print value" line
Why does the second one fail, given that the only difference is an assignment which comes after the print statement? I am pretty confused about this.

The issue is that Python wants you to be explicit and you want to be implicit. The execution model that Python uses binds names to the nearest available enclosing scope.
def Test(value):
# Local Scope #1
def innerFunc():
# Local Scope #2
print value
# No immediate local in Local Scope #2 - check up the chain
# First find value in outer function scope (Local Scope #1).
# Use that.
innerFunc()
def TestWithAssignment(value):
# Local Scope #1
def innerFunc():
# Local Scope #2
print value
# Immediate local variable found in Local Scope #2.
# No need to check up the chain.
# However, no value has been assigned to this variable yet.
# Throw an error.
value = "Changed value"
innerFunc()
There is not (as far as I know) a way to walk up the scope in Python 2.x - you have globals() and locals() - but any scopes between the global and the local scope cannot be accessed (if this is not true, I'd love to be corrected).
However, you can pass the local variable value into your inner local scope:
def TestWithAssignment(value):
def innerFunc(value):
print value
# Immediate local **and assigned now**.
value = "Changed value"
# If you need to keep the changed value
# return value
innerFunc(value)
# If you need to keep the changed value use:
# value = innerFunc(value)
In Python 3 you have the new nonlocal statement that can be used to refer to the containing scope (Thanks #Thomas K).
def TestWithAssignment(value):
def innerFunc():
nonlocal value
print value
value = "Changed value"
innerFunc()

This should fix it:
def Test(value):
def innerFunc(value):
print value
innerFunc(value)

Related

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_

values arguments in pdg

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.

python - why is it possible to use a variable in a function that is declared in the main scope and is not global

I recently noticed something that I was not expected with python, it is possible to check/access a variable inside a function that is declared in the main scope, e.g.:
def my_func(mval):
print ("parameter:", mval)
print ("myvar:", myvar)
#myvar = "value" this is not allowed
print ("IN MAIN")
myvar = "123"
my_func(999)
Output:
IN MAIN
parameter: 999
myvar: 123
Why my_func can access the myvar? And then why it fails when
trying to change the value?
Is there a way to prevent this behavior,
so in this case it would say variable myvar is not defined?
I know we could use global to make this work and it would make sense since we are explicitly saying there is a variable outside of the function scope that we want to access.
I find this tricky because it could lead to errors..
myvar is in the global scope so its global whether you declare it or not. When read, python looks in the function's local scope and then falls back to the module global scope where the variable is defined. When written, python has a problem. Should it assign the variable in the local function scope or the global scope? The global keyword is used in the function and tells that function how to resolve that problem. The variable is never declared global, the function is told to treat that variable as global.
def my_func(mval):
global myval
myval = 'foo' # set in global scope
myval2 = 'bar' # set in local scope
When you display the content of myvar in my_func with the exact code you gave as reference, it will check the global variable table to see if it was previously defined in the execution, which is why it works. Then, when you try to assign a value to it in the function by uncommenting myvar = "value", it tries to define myvar as a local variable of my_func, which shadows the reference to the global instance of myvar.
I'm not exactly sure how using the global keyword would cause any issue, but it should be working as intended. Here is an example of how you can use it:
def my_func(mval):
global myvar
print ("parameter:", mval)
print ("myvar:", myvar)
myvar = "value"
print ("IN MAIN")
myvar = "123"
my_func(999)
print("myvar after my_func:", myvar)
Output:
IN MAIN
parameter: 999
myvar: 123
myvar after my_func: value
If you really don't want to use the global keyword, you can achieve the desired behavior by passing myvar as a parameter of my_func and return the modified value to reassign it in the main scope:
def my_func(mval, myvar):
print ("parameter:", mval)
print ("myvar:", myvar)
myvar = "value"
return myvar
print ("IN MAIN")
myvar = "123"
myvar = my_func(999, myvar)
print ("myvar after my_func:", myvar)
Output:
IN MAIN
parameter: 999
myvar: 123
myvar after my_func: value
If my_func was already built to return a value, remember that you can return multiple values in Python. Therefore, assuming my_func would return a variable called returnvar, the above code could be written as:
def my_func(mval, myvar):
returnvar = mval + 1
print ("parameter:", mval)
print ("myvar:", myvar)
myvar = "value"
return myvar, returnvar
print ("IN MAIN")
myvar = "123"
myvar, returnvar = my_func(999, myvar)
print ("myvar after my_func:", myvar)
print ("returnvar:", returnvar)
Output:
IN MAIN
parameter: 999
myvar: 123
myvar after my_func: value
returnvar: 1000
When you define your function it is not evaluated for correctness only for syntax, so nothing is raised. Once you call the function myvar is already defined in the global scope so the function is valid. Also if you uncomment myvar = 'value' my_func will also evaluate without exception as long as myvar is define before the function is called.

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.

Python and closed variables [duplicate]

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.

Categories

Resources