Scope of variables in python - python

I just come across a confusion ,let's consider the below 2 codes
first code:
def out(): #func1
print i
i=5
Output:5
Second:
def inc(): #func2
i=i+1
print i
i=5
When execiting the above programs ,func1 doesn't give any error but func2 gives an error...
error: var i referenced before assignment
I am confused that i is a local variable or global.If it is local variable then how func1 is accessing it and if it's global then why func2 is unable to access it?

That depends, when you assign a variable inside a function using the = operator, it will shadow any other outer scopes declaration of the variable and use local instead, unless it's declared global inside the function.
For example, your second try will work as you expected if you would do it like this:
def inc(): #func2
global i
i=i+1
print i
i=5
On the other side, if no assignment is performed inside a function, it will use outer scopes declaration, as you saw in your first try.
Also, if you use global i but no i is defined in outer scopes the function will raise a not defined exception as well.

You need to declare i as global in func2 to be able to modify it's value.
def func_2():
global i
i=i+1
print i
Without the global statement, you would be incrementing a variable local to func_2 without actually first giving it a value. And that's where your error comes from.
You can print the contents of global variables without declaring them global. You can also access mutable containers like lists and dictionaries without the global statment but strings, integers, floats and so forth cannot be modified without first making them global.
Examples:
>>> i = [1, 2, 3]
>>> def func_3():
... i.append(4)
... print i
>>> func_3()
[1, 2, 3, 4]
>>> i = "foo"
>>> def func_4()
... i = "bar"
... print i
>>> func_4()
"bar"
>>> print i
"foo"

Related

modify locals() like globals(), to insert variables into scope

(My python version is 3.7.0 on Ubuntu 19.10)
locals() and globals() produce dict-like objects that contain the references that are currently in scope. When a new key is added to globals(), it becomes available in global scope:
# the first two lines can be deleted
# they are just to make sure that I don't accidentally
# have a definition of a left in global scope
a = 1
del a
globals().update({'a':1})
print(a) # prints 1
This also works from inside a function definition. Please note, at the time when the function was defined, there was no global variable a. It is only created when the function is executed:
del a
def fun():
globals().update({'a':1})
print(a)
fun() # prints 1
This does not work with locals():
def fun():
locals().update({'a':1})
print(a)
fun() # NameError: name 'a' is not defined
Curiously, if I define a and then delete it, the error changes:
def fun():
a = 1
del a
locals().update({'a':1})
print(a) # UnboundLocalError: local variable 'a' referenced before assignment
fun()
And, although it is possible to update globals() and then to access the new reference inside a function, this is no longer possible, once a reference of the same name has at some point been in local scope:
def fun():
a = 1
assert 'a' in locals()
del a
assert 'a' not in locals()
globals().update({'a':1})
print(a) # UnboundLocalError
fun()
And another test, regarding the behaviour of locals() in global scope, out of a function:
a = 1
del a
locals().update({'a':1})
print(a) # prints 1
What are the semantics here? Why do the error messages change between NameError and UnboundLocalError? Is it possible to insert a reference into local scope?

Why do I have do "global" a variable that's already global? [duplicate]

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

Why Python allows reading global variables in functions without declaring them as such? [duplicate]

This question already has answers here:
UnboundLocalError trying to use a variable (supposed to be global) that is (re)assigned (even after first use)
(14 answers)
Short description of the scoping rules?
(9 answers)
Closed 6 months ago.
If I run the following code:
x = 1
class Incr:
print(x)
x = x + 1
print(x)
print(x)
It prints:
1
2
1
Okay no problems, that's exactly what I expected. And if I do the following:
x = 1
class Incr:
global x
print(x)
x = x + 1
print(x)
print(x)
It prints:
1
2
2
Also what I expected. No problems there.
Now if I start making an increment function as follows:
x = 1
def incr():
print(x)
incr()
It prints 1 just as I expected. I assume it does this because it cannot find x in its local scope, so it searches its enclosing scope and finds x there. So far no problems.
Now if I do:
x = 1
def incr():
print(x)
x = x + 1
incr()
This gives me the following error in the traceback:
UnboundLocalError: local variable 'x' referenced before assignment.
Why does Python not just search the enclosing space for x when it cannot find a value of x to use for the assignment like my class Incr did? Note that I am not asking how to make this function work. I know the function will work if I do the following:
x = 1
def incr():
global x
print(x)
x = x + 1
print(x)
incr()
This will correctly print:
1
2
just as I expect. All I am asking is why it doesn't just pull x from the enclosing scope when the keyword global is not present just like it did for my class above. Why does the interpreter feel the need to report this as an UnboundLocalError when clearly it knows that some x exists. Since the function was able to read the value at x for printing, I know that it has x as part of its enclosing scope...so why does this not work just like the class example?
Why is using the value of x for print so different from using its value for assignment? I just don't get it.
Classes and functions are different, variables inside a class are actually assigned to the class's namespace as its attributes, while inside a function the variables are just normal variables that cannot be accessed outside of it.
The local variables inside a function are actually decided when the function gets parsed for the first time, and python will not search for them in global scope because it knows that you declared it as a local variable.
So, as soon as python sees a x = x + 1(assignment) and there's no global declared for that variable then python will not look for that variable in global or other scopes.
>>> x = 'outer'
>>> def func():
... x = 'inner' #x is a local variable now
... print x
...
>>> func()
inner
Common gotcha:
>>> x = 'outer'
>>> def func():
... print x #this won't access the global `x`
... x = 'inner' #`x` is a local variable
... print x
...
>>> func()
...
UnboundLocalError: local variable 'x' referenced before assignment
But when you use a global statement then python for look for that variable in global scope.
Read: Why am I getting an UnboundLocalError when the variable has a value?
nonlocal: For nested functions you can use the nonlocal statement in py3.x to modify a variable declared in an enclosing function.
But classes work differently, a variable x declared inside a class A actually becomes A.x:
>>> x = 'outer'
>>> class A:
... x += 'inside' #use the value of global `x` to create a new attribute `A.x`
... print x #prints `A.x`
...
outerinside
>>> print x
outer
You can also access the class attributes directly from global scope as well:
>>> A.x
'outerinside'
Using global in class:
>>> x = 'outer'
>>> class A:
... global x
... x += 'inner' #now x is not a class attribute, you just modified the global x
... print x
...
outerinner
>>> x
'outerinner'
>>> A.x
AttributeError: class A has no attribute 'x'
Function's gotcha will not raise an error in classes:
>>> x = 'outer'
>>> class A:
... print x #fetch from globals or builitns
... x = 'I am a class attribute' #declare a class attribute
... print x #print class attribute, i.e `A.x`
...
outer
I am a class attribute
>>> x
'outer'
>>> A.x
'I am a class attribute'
LEGB rule: if no global and nonlocal is used then python searches in this order.
>>> outer = 'global'
>>> def func():
enclosing = 'enclosing'
def inner():
inner = 'inner'
print inner #fetch from (L)ocal scope
print enclosing #fetch from (E)nclosing scope
print outer #fetch from (G)lobal scope
print any #fetch from (B)uilt-ins
inner()
...
>>> func()
inner
enclosing
global
<built-in function any>
From Python scopes and namespaces:
It is important to realize that scopes are determined textually: the global scope of a function defined in a module is that module’s namespace, no matter from where or by what alias the function is called. On the other hand, the actual search for names is done dynamically, at run time — however, the language definition is evolving towards static name resolution, at “compile” time, so don’t rely on dynamic name resolution! (In fact, local variables are already determined statically.)
Which means that, the scope for x = x + 1 is determined statically, before the function is called. And since this is an assignment, then 'x' becomes a local variable, and not looked up globally.
That is also the reason why from mod import * is disallowed in functions. Because the interpreter won't import modules for you in compile time to know the names you are using in the function. i.e, it must know all names referenced in the function at compile time.
It's the rule Python follows - get used to it ;-) There is a practical reason: both the compiler and human readers can determine which variables are local by looking only at the function. Which names are local has nothing to do with the context in which a function appears, and it's generally a Very Good Idea to follow rules that limit the amount of source code you have to stare at to answer a question.
About:
I assume it does this because it cannot find x in its local scope, so
it searches its enclosing scope and finds x.
Not quite: the compiler determines at compile time which names are and aren't local. There's no dynamic "hmm - is this local or global?" search going on at runtime. The precise rules are spelled out here.
As to why you don't need to declare a name global just to reference its value, I like Fredrik Lundh's old answer here. In practice, it is indeed valuable that a global statement alerts code readers to that a function may be rebinding a global name.
Because that's the way it was designed to work.
Basically, if you have an assignment anywhere in your function, then that variable becomes local to that function (unless you've used global, of course).
Because it would lead to very hard to track down bugs!
When you type that x = x + 1, you could have meant to increment an x in the enclosing scope... or you could have simply forgotten that you already used x somewhere else and been trying to declare a local variable.
I would prefer the interpreter to only allow you to change the parent namespace if you intend to- this way you can't do it by accident.
I can try and make an educated guess why it works this way.
When Python encounters a string x = x + 1 in your function, it has to decide where to look up the x.
It could say "the first occurrence of x is global, and the second one is local", but this is quite ambiguous (and therefore against Python philosophy). This could be made part of the syntax, but it potentially leads to tricky bugs. Therefore it was decided to be consistent about it, and treat all occurrences as either global or local variables.
There is an assignment, therefore if x was supposed to be global, there would be a global statement, but none is found.
Therefore, x is local, but it is not bound to anything, and yet it is used in the expression x + 1. Throw UnboundLocalError.
As an extra example to the newly created A.x created within the class. The reassigning of x to 'inner' within the class does not update the global value of x because it is now a class variable.
x = 'outer'
class A:
x = x
print(x)
x = 'inner'
print(x)
print(x)
print(A.x)

list assignment to global variable

Please look at this code:
def x():
a.append(1)
def y():
print a
a = []
x()
y()
which outputs:
[1]
and this:
def x():
a = [1]
def y():
print a
a = []
x()
y()
which outputs:
[]
I don't understand why I can't explicitly set global list variable a in a function, without using list methods, like .append(), but just setting the value?
In your second example, a = [1] is creating a new local variable named a, not referencing the global variable a. In order to reference the outer scoped a you could do:
def x():
global a
a = [1]
See the Python documentation for the global keyword, specifically:
It would be impossible to assign to a global variable without global,
although free variables may refer to globals without being declared
global.
In your first example, a uses the global variable because you are referencing a variable that does not exist as a local in the x() function, but does exist in the global variables.
In version 1, when you use a in x and y Python "goes looking" for an a to use, and finds it in the outer scope.
In version 2, you define a local a inside x, and assign to that; there's no need for Python to look in other scopes. If you want to use the "outside a", you have to be explicit about your intentions:
def x():
global a
a = [1]
the second example isn't a global variable. You are declaring a new local variable a, and assigning it to [1]

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