python global variable, whats going on here? Explanation needed - python

Can someone explain what's going on here
x = 10
def foo():
print "x in foo = ",x
if x: x = 8 -------------> mysterious line
foo()
print "x in main = ",x
In the above code, If i comment out the mysterious line (if x: x = 8)
I get the output
x in foo = 10
x in main = 10
Otherwise I end up with error
"UnboundLocalError: local variable 'x' referenced before assignment"
Why so?
I know global x is helpful only when I need to modify a global variable locally.

As soon as you make an assignment to x anywhere in the function, it becomes a local variable, even if there are references to its value prior to the assignment.

In your function, you assign to x with no global declaration, so x is a local variable. However, you attempt to read x in the function before it's assigned to, so you have an unbound local at that point.

What may seem strange at first is that local names are detected statically: if the name x is assigned anywhere in the function, Python assumes that x is a local name everywhere in the function.
In other words, the line order in your function is misleading: as long as x is assigned somewhere in the function, the first line's x becomes a reference to a local variable (which at this code point is still unbound).

Like Wooble already said, you need to declare the modifikation of a global variable before usage.
def foo():
global x
print "x in foo = ",x
if x: x = 8

x = 8 anywhere in the function without global x makes it a local variable. print x fails because a local variable is not initialized

Related

Scoping rules in 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.

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)

Local variable referenced before assignment error python

def outside(x=1):
def printHam():
x = x+1
print x
return printHam
myfunc = outside(7)
myfunc()
This doesn't works gives error Local variable referenced before assignment error python
However this works
def outside(x=1):
def printHam():
print x + 1
return printHam
myfunc = outside(7)
myfunc()
Because you are assigning x in the first case, python will assume x is a local variable (which is the default). In the second case you aren't assigning it so it will check the global scope.
If you want this example to work, you have to pass the outer x into the inner function like so:
def outside(x=1):
def printHam(x=x):
x = x+1
print x
return printHam
That being said, this seems like a horribly contrived use case, so there's probably a better solution for your actual use case. But I can't tell you what it is without knowing more about what you're trying to do.

Return doesn't work (python) [duplicate]

This question already has answers here:
How do I get a result (output) from a function? How can I use the result later?
(4 answers)
Closed 3 years ago.
I've been having some problems with the return statement and I can't seem to figure out what's wrong. It seemed to work fine yesterday, but today no function that contains it seems to work properly. Here's an example of what's going on:
def fpol(x):
y=x**4
return(y)
If I then type in
fpol(4)
I'm given the answer 256 (as I would expect). If I then type in
print(y)
or try to use/view y in any way, I'm told
NameError: name 'y' is not defined
I've also tried it with return(y) being replaced by return y . I can also insert print(y) into the original function and that works fine, so I know that during the function, y actually does have a value, it's just not being returned. Any help is much appreciated.
Edit: I've now been able to work past the issue I had with the return function. Thanks to everyone who responded.
y is known only in the scope of the function fpol. You should assign the result to a variable, and only then print its value:
y = fpol(4)
print(y)
Note that y is a different variable here, it has nothing to do with the y inside the function. You could write:
x = fpol(4)
print(x)
I suspect you are trying to print(y) outside the function. The variable y is local in scope, that is only defined within fpol(). So you can print it there. You can do:,
def fpol(x):
y=x**4
return(y)
y = fpol(4)
print(y)
But not:
def fpol(x):
y=x**4
return(y)
print(y)
The variable y is only visible from within the function you have declared. To print the result of fpol(4) you can assign the returned value to a new variable:
returnedValue = fpol(4)
print(returnedValue)
I guess you need to store the value returned by the function in a variable and then print it:
y = fpol(4)
print y
y is out of scope. It was only in scope for your function call, and since the function fpol has ran and ended, the scope has died with it. We need to assign a variable that's visible to the print command. Let's reuse y for simplicity.
y = fpol(4)
print(y)
The key rule of thumb for python is every time you indent you have started a new scope! You must make sure your variables are in scope to use them.
y is a variable with scope that's local to function fpol().
it is not defined outside of that scope.
The code return y does not make y visible outside of the function in which it has been defined. It only returns the value of y to the caller as a function return value.
Using your example, the following would show you the value of y
def fpol(x):
y=x**4
print(y) # This will print the value of y
return(y)
print fpol(4) # This will print the returned result
But trying to print y after your function call will fail with the not defined error as the variable is defined only inside you function, i.e. it is local to that function. This is often referred to as the scope of a variable.
As soon as the function returns, y ceases to exist.
y is not defined outside th function
you can not do print(y)
probably you want
y = fpol(4)
print(y)
is not very good programming style, but you also can make y global variable. then it will be available after function, but pls do not do it.
finally you can do just
return y
no need ()

Can someone please explain this bit of Python code?

I started working in Python just recently and haven't fully learned all the nuts and bolts of it, but recently I came across this post that explains why python has closures, in there, there is a sample code that goes like this:
y = 0
def foo():
x = [0]
def bar():
print x[0], y
def change(z):
global y
x[0] = y = z
change(1)
bar()
change(2)
bar()
change(3)
bar()
change(4)
bar()
foo()
1 1
2 2
3 3
and basically I don't understand how it actually works, and what construct like x[0] does in this case, or actually I understand what it's doing, I just don't get how is it this :)
Before the nonlocal keyword was added in Python 3 (and still today, if you're stuck on 2.* for whatever reason), a nested function just couldn't rebind a local barename of its outer function -- because, normally, an assignment statement to a barename, such as x = 23, means that x is a local name for the function containing that statement. global exists (and has existed for a long time) to allow assignments to bind or rebind module-level barenames -- but nothing (except for nonlocal in Python 3, as I said) to allow assignments to bind or rebind names in the outer function.
The solution is of course very simple: since you cannot bind or rebind such a barename, use instead a name that is not bare -- an indexing or an attribute of some object named in the outer function. Of course, said object must be of a type that lets you rebind an indexing (e.g., a list), or one that lets you bind or rebind an attribute (e.g., a function), and a list is normally the simplest and most direct approach for this. x is exactly that list in this code sample -- it exists only in order to let nested function change rebind x[0].
It might be simpler to understand if you look at this simplified code where I have removed the global variable:
def foo():
x = [0]
def bar():
print x[0]
def change(z):
x[0] = z
change(1)
bar()
foo()
The first line in foo creates a list with one element. Then bar is defined to be a function which prints the first element in x and the function change modifies the first element of the list. When change(1) is called the value of x becomes [1].
This code is trying to explain when python creates a new variable, and when python reuses an existing variable. I rewrote the above code slightly to make the point more clear.
y = "lion"
def foo():
x = ["tiger"]
w = "bear"
def bar():
print y, x[0], w
def change(z):
global y
x[0] = z
y = z
w = z
bar()
change("zap")
bar()
foo()
This will produce this output:
lion tiger bear
zap zap bear
The point is that the inner function change is able to affect the variable y, and the elements of array x, but it does not change w (because it gets its own local variable w that is not shared).
x = [0] creates a new list with the value 0 in it. x[0] references the zero-eth element in the list, which also happens to be zero.
The example is referencing closures, or passable blocks of code within code.

Categories

Resources