Scoping rules in python - python

Consider the following python snippet (I am running Python 3)
name = "Sammy"
def greet():
name = 'johny'
def hello():
print('hello ' + name) # gets 'name' from the enclosing 'greet'
hello()
greet()
This produces the output hello johny as expected
However,
x = 50
def func1():
x = 20
def func2():
print("x is ", x) # Generates error here
x = 2
print("Changed the local x to ",x)
func2()
func1()
print("x is still ",x)
generates an UnboundLocalError: local variable 'x' referenced before assignment.
Why does the first snippet work, whereas the second doesn't?

The error is actually caused (indirectly) by the following line, i.e. x = 2. Try commenting out that line and you'll see that the function works.
The fact that there is an assignment to a variable named x makes x local to the function at compile time, however, at execution time the first reference to x fails because, at the time that the print() statement is executed, it does not exist yet in the current scope.
Correct it by using nonlocal in func2():
def func2():
nonlocal x
print("x is ", x)
x = 2
print("Changed the local x to ",x)
The reason that the first function (greet()) works is because it's OK to read variables in an outer scope, however, you can not assign to them unless you specify that the variable exists in an outer scope (with nonlocal or global).
You can assign to a variable of the same name, however, that would create a new local variable, not the variable in the outer scope. So this also works:
def func1():
x = 20
def func2():
x = 2
print('Inner x is', x)
func2()
print('Outer x is', x)
Here x is assigned to before being referenced. This creates a new variable named x in the scope of function func2() which shadows the x defined in func1().

In a given scope, you can only reference a variable's name from one given scope. Your variable cannot be global at some point and local later on or vice versa.
For that reason, if x is ever to be declared in a scope, Python will assume that you are refering to the local variable everywhere in that scope, unless you explicitly state otherwise.
This is why you first function, greet, works. The variable name is unambiguously coming from the closure. Although, in func2 the variable x is used in the scope and thus you cannot reference the x from the closure unless explicitly stating otherwise with nonlocal.
The following errors might enlighten us on this.
A variable cannot become global after use
def func1():
x = 20
def func2():
print("x is ", x)
global x
print("Changed the local x to ",x)
func2()
This raises a SyntaxError: name 'x' is used prior to global declaration. This means that the closure's x cannot be used and then the global one.
A global variable cannot become local
Here is another case using global at the top of func2.
def func1():
x = 20
def func2():
global x
print("x is ", x)
x = 2
print("Changed the local x to ",x)
func2()
This code was exectued without error, but notice that the assignment to x updated the global variable, it did not make x become local again.

Related

using an uninitialized global variable throws NameError

This code throws NameError: name 'x' is not defined":
def function():
global x
print(x)
function()
print(x)
However, this code works and prints "2" two times:
def function():
global x
x = 2
print(x)
function()
print(x)
My question is, what actually happens at the moment x is declared as global?
You have not defined x, resulting in the errors. Try and make the function have a parameter of x.
def function(x):
print(x)
Also, when you globalize a variable, its scope is not just within the function, but in every function/class.
Carden

Closure and variable scope in python 3.x issue causing me confusion

I am using Python 3.7.0 and I am doing some experiments in order to comprehend the subtleties of variable scopes but I still don't understand this behaviour.
When I execute this code:
def f():
x=0
def g():y=x+1;x=y
return g
f()()
I get UnboundLocalError: local variable 'x' referenced before assignment
However when I execute this one:
def f():
x=0
def g():y=x+1
return g
f()()
It works fine. And even this one:
def f():
x=0
def g():x=1
return g
f()()
It also works fine. So I am confused. It seems to me that if assigning the value 1 to the nonlocal variable x in the g function works fine alone and on the other hand if assigning an expression containing x to a local variable y in the function g works also fine then the instructions y=x+1 and x=y should both work. I don't understand what is causing the error. I feel I am missing something fundamental in my understanding of Python's behaviour.
I'll take an ill-advised crack at this. In the following code:
def f():
x = 0 # assignment makes this x local to f()
def g():
y = x + 1
x = y # assignment makes this x local to g()
return g
f()()
The assignment of x inside g() forces it to be a variable local to g(). So the first line causes the error as you accessing the value of an unassigned local. I believe this "assignment makes it local" logic doesn't follow the flow of your program -- the function is viewed as whole to make this determination and then the code is looked at line by line.
You can access any variable in scope from a function, but you can only set locals. Unless you've declared the variable you're assigning as global or nonlocal:
def f():
x = 0 # assignment makes this x local to f()
def g():
nonlocal x
y = x + 1
x = y # x is declared to come from a nonlocal scope
return g
f()()
In your second example, it's not an issue as you're only looking at the value of x, not setting it, so it can come from any appropriate scope:
def f():
x = 0 # assignment makes this x local to f()
def g():
y = x + 1 # x comes from an outer scope
return g
f()()
Finally, your third example is the same as the first without the unssigned local variable usage error:
def f():
x = 0 # assignment makes this x local to f()
def g():
x = 1 # assignment makes this x local to g()
return g
f()()
It seems to me that if assigning the value 1 to the nonlocal variable
x in the g function works fine alone
How did you determine that the reassignment of x in f() worked fine?

Any type of scope resolution operator in Python?

Is there any way to make the python interpreter choose the global variable specifically when there is also a local variable with the same name? (Like C++ has :: operator)
x=50
def fun():
x=20
print("The value of local x is",x)
global x
#How can I display the value of global x here?
print("The value of global x is",x)
print("The value of global x is",x)
fun()
The second print statement inside the function block should display the value of global x.
File "/home/karthik/PycharmProjects/helloworld/scope.py", line 7
global x
^
SyntaxError: name 'x' is used prior to global declaration
Process finished with exit code 1
Python does not have a direct equivalent to the :: operator (typically that kind of thing is handled by the dot .). To access a variable from an outer scope, assign it to a different name as to not shadow it:
x = 50
def fun():
x = 20
print(x) # 20
print(x_) # 50
print(x) # 50
x_ = x
fun()
But of course Python would not be Python if there weren't a hack around this... What you describe is actually possible, but I do not recommend it:
x = 50
def fun():
x = 20
print(x) # 20
print(globals()['x']) # 50
print(x) # 50
fun()
I don't know any way to do this (and I am not an expert). The two solutions that I can think of are to give another name to your local x (like xLoc), or to put your global x in argument, like this :
x=50
def fun(xGlob):
x=20
print("The value of local x is",x)
print("The value of global x is",xGlob)
print("The value of global x is",x)
fun(x)
Is this answering your question ?

Difference between local variable and global variable

I'm confused on the difference between local variables and global variables. I know that global variables are declared outside a function while local is declared in a function. However, I'm wondering if it is as so:
def func(l):
a = 0
n = len(l)
w = l[0]
while...
My question is, in the function i wrote as an example, i know that a is a local variable but what about the other two? are they local variables too?
l is a location variable that you passed into the function, and so is w since w is a "copy" of l[0]
To have a global variable you need to declare the variable as global using the global keyword.
x = 1
y = 2
def func(l):
global x
x = 4
l = 5
print("x is {0}, y is {1}".format(x,l))
func(y)
print("x is {0}, y is {1}".format(x,y))
Returns:
x is 4, y is 5
x is 4, y is 2
Notice how x is now changed but y isn't?
Note that lists are special because you don't need to declare the global keyword to append or remove from them:
x = []
def func():
x.append(3)
print("x is {0}".format(x))
func()
print("x is {0}".format(x))
Returns:
x is [3]
x is [3]
But references to lists are not global because they are a 'copy' of it :
x = [3]
y = 1
def func():
y = x[0]
print("y is {0}".format(y))
func()
print("y is {0}".format(y))
Returns:
y is 3
y is 1
Also Reassigning the variable that was a list is not global:
x = [3]
def func():
x = [2]
print("x is {0}".format(x))
func()
print("x is {0}".format(x))
Returns:
x is [2]
x is [3]
Also note that there are always a better way to do something than to declare global variables, because as your code scales it will get messier.
All those variables are assigned values and that automatically declares them in the local scope. So they are local variables.
Even if they had been declared outside in the global scope, they would still be local.
Unless you used the global keyword which tells the interpreter that the current scope refers to a previously declared global or creates a new one in the global context if none with the same name exists.
def func(l):
global n # n is declared in the global scope
a = 0
n = len(l)
w = l[0]
while...
func()
print(n)
All the variables in your function are local, usable by that function only. Global variables are usable by all functions in the class. In your function
def func(l):
a = 0
n = len(l)
w = l[0]
while...
>>>a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
a, n, w, and l are not usable outside of the func scope. If you did something like this...
a = 0
def func(l):
n = len(l)
w = l[0]
while...
>>>a
0
I just happened to come across this read which provides you with a lot of detail on variable declaration and scope.
[Python Doc the rule for local and global variables]:
In Python, variables that are only referenced inside a function are
implicitly global. 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. illustrate this with your example:
Use your example to illustrate, all variables declared inside the func are local variables. and even values declared outside of the function. like variable x it has no parent function but it is still actually NOT a global variable. x just get commits to memory before the func gets called
x = 5 #actually local
def func(l):
a = 0
n = len(l)
w = l[0]
print(x) # x is local we call call it, use it to iterate but thats is pretty much it
if we try to modify the local variable x you would get an error:
x = 5
def func(l):
a = 0
n = len(l)
w = l[0]
x = x + 1 # can't do this because we are referning a variable before the funtion scope. and `x` is not global
UnboundLocalError: local variable 'x' referenced before assignment
but now if we define x as global then we can freely modify it as Python know anywhere when we call x is references to the same variable and thus the same memory address
x = 5
def func():
global x
x = x + 1
func()
print(x)
it prints out : 6 for the value of x
Hope through this example you see that global variable can be accessed anywhere in the program. Whereas local variable can only be accessed within its function scope. Also, remember that even though global variables can be accessed locally, it cannot be modified locally inherently.
Local Variable : When we declare a variable inside a function, it becomes a local variable.
global variable : When we declare a variable outside a function , it becomes a global variable.
A python program understand difference local vs global variable
#same name for local and global variable.
a = 1 #this is global variable
def my_function():
a = 2 #this is local variable
print("a= ", a) #display local var
my_function()
print("a = ", a) #display global var.
This program to access global variable
a = 1 #This is global var
def my_function():
global a #This is global var.
print("global a= ", a) #display new value
a = 2 #modify global var value
print("modify a = ", a) #display a new value
my_function()
print("global a= ", a) #display modified value

In Python what is a global statement?

What is a global statement? And how is it used? I have read Python's official definition;
however, it doesn't make a lot of sense to me.
Every "variable" in python is limited to a certain scope. The scope of a python "file" is the module-scope. Consider the following:
#file test.py
myvariable = 5 # myvariable has module-level scope
def func():
x = 3 # x has "local" or function level scope.
Objects with local scope die as soon as the function exits and can never be retrieved (unless you return them), but within a function, you can access variables in the module level scope (or any containing scope):
myvariable = 5
def func():
print(myvariable) # prints 5
def func2():
x = 3
def func3():
print(x) # will print 3 because it picks it up from `func2`'s scope
func3()
However, you can't use assignment on that reference and expect that it will be propagated to an outer scope:
myvariable = 5
def func():
myvariable = 6 # creates a new "local" variable.
# Doesn't affect the global version
print(myvariable) # prints 6
func()
print(myvariable) # prints 5
Now, we're finally to global. The global keyword is the way that you tell python that a particular variable in your function is defined at the global (module-level) scope.
myvariable = 5
def func():
global myvariable
myvariable = 6 # changes `myvariable` at the global scope
print(myvariable) # prints 6
func()
print(myvariable) # prints 6 now because we were able
# to modify the reference in the function
In other words, you can change the value of myvariable in the module-scope from within func if you use the global keyword.
As an aside, scopes can be nested arbitrarily deeply:
def func1():
x = 3
def func2():
print("x=",x,"func2")
y = 4
def func3():
nonlocal x # try it with nonlocal commented out as well. See the difference.
print("x=",x,"func3")
print("y=",y,"func3")
z = 5
print("z=",z,"func3")
x = 10
func3()
func2()
print("x=",x,"func1")
func1()
Now in this case, none of the variables are declared at the global scope, and in python2, there is no (easy/clean) way to change the value of x in the scope of func1 from within func3. That's why the nonlocal keyword was introduced in python3.x . nonlocal is an extension of global that allows you to modify a variable that you picked up from another scope in whatever scope it was pulled from.
mgilson did a good job but I'd like to add some more.
list1 = [1]
list2 = [1]
def main():
list1.append(3)
#list1 = [9]
list2 = [222]
print list1, list2
print "before main():", list1, list2
>>> [1] [1]
main()
>>> [1,3] [222]
print list1, list2
>>> [1, 3] [1]
Inside a function, Python assumes every variable as local variable
unless you declare it as global, or you are accessing a global variable.
list1.append(2)
was possible because you are accessing the 'list1' and lists are mutable.
list2 = [222]
was possible because you are initializing a local variable.
However if you uncomment #list1 = [9], you will get
UnboundLocalError: local variable 'list1' referenced before assignment
It means you are trying to initialize a new local variable 'list1' but it was already referenced before,
and you are out of the scope to reassign it.
To enter the scope, declare 'list1' as global.
I strongly recommend you to read this even though there is a typo in the end.
a = 1
def f():
a = 2 # doesn't affect global a, this new definition hides it in local scope
a = 1
def f():
global a
a = 2 # affects global a
Basically it tells the interpreter that the variable its given should be modified or assigned at the global level, rather than the default local level.
You can use a global variable in other functions by declaring it as global in each function that modifies it
Python wants to make sure that you really know that's what you're playing with by explicitly requiring the global keyword.
See this answer

Categories

Resources