Local and global references with UnboundLocalError - python

I don't quite understand why the code
def f():
print(s)
s = "foo"
f()
runs perfectly fine but
def f():
print(s)
s = "bar"
s = "foo"
f()
gives me UnboundLocalError. I know that I can fix this by declaring s as a global variable inside the function or by simply passing s an an argument into the function.
Still I don't understand how python seemingly knows whether or not s is referenced inside the function before the line has been executed? Does python make some sort of list of all local variable references when the function is read into the global frame?

Other answers have focused on the practical aspects of this but have not actually answered the question you asked.
Yes, the Python compiler tracks which variables are assigned when it is compiling a code block (such as in a def). If a name is assigned to in a block, the compiler marks it as local.Take a look at function.__code__.co_varnames to see which variables the compiler has identified.
The nonlocal and global statements can override this.

Yes, Python will look-ahead to recover all variables declared in the local scope. These will then overshadow global variables.
So in your code:
def f():
print(s)
s = "foo"
f()
Python did not find s in the local scope, so it tries to recover it from the global scope and finds "foo".
Now in the other case the following happens:
def f():
print(s)
s = "bar
s = "foo"
f()
Python knows that s is a local variable because it did a look-ahead before runtime, but at runtime it was not assigned yet so it raised and exception.
Note that Python will even let you reference variables that have not been declared anywhere. If you do:
def foo():
return x
f()
You will get a NameError, because Python, when not finding, x as a local variable will just remember that at runtime it should look for a global variable named x and then fail if it does not exist.
So UnboundLocalError means that the variable may eventually be declared in scope but has not been yet. On the other hand NameError means that the variable will never be declared in the local scope, so Python tried to find it in the global scope, but it did not exist.

Related

Why Python "global" statement is always evaluated, even under conditional statement that is not met?

I don't know how to explain the case, just try for yourself:
x = 7
def f():
if False:
global x
print("global")
x += 1
f()
print(x)
In my opinion, the above code should result in "UnboundLocalError: local variable 'x' referenced before assignment", but instead it not only works, but it changes the global variable 'x'.
A function gets a local namespace every time it is called. Python needs a way to know which variables are defined in that namespace or a containing namespace. Instead of requiring that all local variables be declared local, python's rule is that any variable that is assigned in a function is local to that function. It figures this out at compile time.
The global keyword does the opposite. It tells python that in this single function, assignment to the "global" variable is really an assignment in the enclosing namespace. Like local variables, this is figured out when the function is compiled. Because of compilation issues, python requires that the global be declared before the first reference to the variable. This is a compile thing, not a runtime thing, so its okay for it to be in a block that isn't ever really run.

Why the message is "name is not defined" rather than "variable referenced before assignment" in global scope?

One unreachable assignment statement made one "defined but not assigned" name in local scope but this is not for global scope:
#!/usr/bin/python3
#global scope
if False:
baz = 4
pass
#Raise "NameError: name 'baz' is not defined"
print(baz)
I expect get the message similar to variable 'baz' referenced before assignment in global scope as in local scope but actual message is name 'baz' is not defined in global scope.
I running in Python 3.7.2+.
In a function scope, Python analyzes all assignment operations to produce a complete list of the variables that scope should have. A local variable lookup is known at bytecode compilation time to correspond to at least one actual assignment to that variable in the function body, so the exception message for an unbound local variable mentions assignments.
No such analysis happens at global or class scope, and no such analysis could possibly be comprehensive, due to operations like from whatever import * and globals().update(stuff) that bind variables unknown at compile time. Python makes no attempt to keep track of what variables the global scope "should" have, and it doesn't try to distinguish between
print(a)
a = 1
where the variable is used before binding, and
print(b)
where there is no binding operation at all, or
thing = 1
print(thinh)
where thinh is a typo. The same error message template is used whether or not there's an assignment, so it wouldn't make as much sense for the template to say "before assignment" when there might not be an assignment at all.
When you see NameError:, Python is trying to use a variable or function based on a previous definition. If you don't define your variable you will get this error.
In your code because your variable is only defined when you have declared something False Python can not pass it through globally to the rest of your code. You can make baz global by defining it before your false statement. There are a number of ways to achieve it however it is dependent on what your goal is. Here is an example:
baz = 0
if False:
baz = 4
print(baz)
Output: 0
Then when True:
baz = 0
if True:
baz = 4
print(baz)
Output: 4

Static/Dynamically Scoped,Typed,Binding

I am asking this just to clarify if i am thinking right.
Statically/Dynamically typed
A language is statically typed if the type of a variable is known at compile time. This in practice means that you as the programmer must specify what type each variable is. Example: Java, C, C++.
A language is dynamically typed if the type of a variable is interpreted at runtime. This means that you as a programmer can write a little quicker because you do not have to specify type everytime. Example: Perl
Static/Dynamic Binding-which the following link clearly explains the difference
Static Binding and Dynamic Binding
The main thing that i want to ask starts from here.
I know the difference between Static Scoping and Dynamic Scoping.
However as i was going through stack overflow people said that C++ and Python are Statically Scoped.
In c++ if i type
int void main(){
cout<<i;
int i=15;
}
int i=10;
It works(even in java).However its python equivalent
def foo():
print(x)
x=10
x='global'
foo()
Gives an error.
The scope is static in Python as well as in C++. The difference between those languages is related to rules that define start and end of the scope of names.
In C++ the variable i is considered as global before the local definition int i=15;.
In Python:
What are the rules for local and global variables in Python?:
If a variable is assigned a value anywhere within the function’s body,
it’s assumed to be a local unless explicitly declared as global.
Naming and binding:
When a name is used in a code block, it is resolved using the nearest
enclosing scope.
So, since the variable x is assined inside foo() it is assumed to be a local variable starting from the beginning of the function foo().
It is possible to treat x within entire function block as global using the global keyword:
def foo():
global x # name 'x' refers to the global variable
print(x) # print global 'x'
x=10 # assign global variable
x='global'
foo() # print 'global'
foo() # print '10'
Actually it is possible to access the global variable x even if you want to have a local name x in the same function using globals() build-it function:
def foo(): # name 'x' refers to the local variable
print(globals()['x']) # access the global name 'x'
x=10 # assign local variable
print(x) # print local 'x'
x='global'
foo()

Python: when globals().update works and when it doesn't

I am aware of the fact that updating locals() or globals() should be avoided if possible.
As far as I understand, updating locals() in a function normally results in a NameError when referencing an updated variable, as already discussed here:
How can I load all keys from a dict as local variables, a better aproach?
My understanding is that the interpreter creates a static lookup table for local variables, and thus changes to locals() and not visible at runtime.
How about updating globals() then? This doesn't seem to be a terribly great idea either. Why does the following code fail:
def foo():
globals().update({'a': 1})
print a
if False: a = 0
>>> foo()
UnboundLocalError: local variable 'a' referenced before assignment
while removing the never-to-be-executed if False: a = 0 assignment works?
If the interpreter creates a look up table for performance reasons, shouldn't it take statements that will not be executed during runtime into account?
This has nothing to do with your update to the globals dict specifically. You'd get exactly the same result if you did this:
a = 1
def foo():
print a
if False: a = 0
foo()
That's because any assignment to a variable that's not explicitly marked as global within a scope makes the variable local throughout that scope.

Confusion about Python variable scope

I came across some code which kind of puzzled me. Here's a minimal example which shows this:
# of course, the ... are not part of the actual code
some_var = {"key1":"value1" ... "keyN":"valueN"}
def some_func():
v = some_var["key1"]
The code works, but the fact that I can access some_var directly confuses me. The last time I had to write some Python code, I remember having to write some_func like this:
def some_func():
global some_var
v = some_var["key1"]
I am using Python 2.7.1 on a Windows 7 PC. Did something change in the 2.7 release that allows for this?
No, you just can't reassign some_var in a local scope. Consider the following:
some_var = {}
def some_func():
# some_var[5] = 6
some_var = {1:2}
some_var[3] = 4
some_func()
print (repr(some_var)) # {}
You'll see the assignment in some_func actually creates a local variable which shadows the global one. Therefore, uncommenting the line would result in an UnboundLocalError - you can't access variables before they're defined.
There's a difference between using (e.g. calling or using in an expression) a name from an outer scope and assigning it (and there's a difference between assigning a bare variable and assigning a member of an object pointed to by a variable - x.y = ... and x[...] = ... count as method calls!).
You only need to declare that a variable is from an outer scope if you're re-assigning it. In Python 2, you can only do that with global variables (via global var), in Python 3 you can do it for abritarily nested scopes (e.g. closures) using nonlocal var.
Using a nonlocal variable as in you example doesn't require global. It does, however, as soon as you assign the variable (with the aforementioned definition of assignment) anywhere within that scope - so add a line some_var = ... after the line where you're using it and you'll get an UnboundLocalError. Refer to the documentation for the nitty gritty details.
You only have to use global if you want to assign a new value to that variable.
Nested scope was introduced in Python 2.1 (and enabled by default in Python 2.2) (emphasis mine):
Put simply, when a given variable name is not assigned a value within a function (by an assignment, or the def, class, or import statements), references to the variable will be looked up in the local namespace of the enclosing scope. A more detailed explanation of the rules, and a dissection of the implementation, can be found in the PEP.
You only need to use global if you intend to assign to the variable, for reading the variable this is not necessary. This difference is not arbitrary, though it may seem like it at first glance.
When reading a value the interpreter can just look for a local variable named some_var, if it cannot find it then it looks for a global variable of that name. These are simple and straight forward semantics.
When assigning values to a variable the interpreter needs to know whether you intend to assign to a local variable some_var or a global variable. The interpreter assumes that some_var = 2 when called within a function is assigning to a local variable, this makes sense since this is the most common case. For the relatively rare times when you want to assign to a global variable from within a function then you use the global modifier global some_var = 2.
It depends on the usage of the variable in the function
Python variable scope error
Assigning a value to a name makes the name local, unless the name is explicitly declared global.
a = 12
def foo():
a = 42
print a # uses local
foo()
>>> 42
def foo():
global a
a = 42
foo()
print a
>>> 42
If a name is not assigned to, it is global.
a = 12
def foo():
print a # uses global
foo()
>>> 12
In short, you only have to explicitly declare a name global if you will be assigning to it. If you are just reading from it, you can use it at will. However, if you ever assign to the variable, it will be considered local in that function unless you declared it global.
b = 5
def foo():
print b
b = 7
foo()
>>> ???
Since b is assigned to in foo() and not declared global, Python decides at compile time that b is a local name. Therefore b is a local name throughout the whole function, including at the print statement before the assignment.
Therefore the print statement gives you an error, because the local name b has not been defined!

Categories

Resources