Why can you read, but not modify, global values? - python

>>> import sys
>>> print(sys.version)
2.4.4
>>> b = 11
>>> def foo2():
... a = b
... print a, b
...
>>> foo2()
11 11
>>> def foo3():
... a = b
... b = 12
... print a, b
...
>>> foo3()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in foo3
UnboundLocalError: local variable 'b' referenced before assignment
>>> def foo4():
... global b
... a = b
... b = 12
... print a, b
...
>>> foo4()
11 12
Question> In foo3, why you can access global variable without declaring it but you still cannot modify it.

Without a global declaration, the Python compiler scans the whole code of each function to see which variables are assigned to within the function code. In foo3(), you assign to both a and b so therefore they are both treated as local variables within the function.
When the method code executes, at the point where you do a = b, b does not have a value yet (because you have not assigned anything to it). Therefore, you get an UnboundLocalError.
This is done so that the use of a variable within a function always refers to the same location, even if nothing has been assigned to it yet.

Accidentally stomping on a global variable is a frequent source of error. So it makes sense to declare a variable global before you modify it.
Having to always declare a non-local variable global is a pain. So you're allowed to just use a global variable if all you do is read it. But if you're going to modify x anywhere in your function, you must declare it global before you do anything with it.

Related

Python global variable name to be treated locally inside function

>>> x = 1
>>> def f():
... print x
...
>>> f()
1
>>> x = 1
>>> def f():
... x = 3
... print x
...
>>> f()
3
>>> x
1
>>> x = 1
>>> def f():
... print x
... x = 5
...
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in f
UnboundLocalError: local variable 'x' referenced before assignment
>>> x = 1
>>> def f():
... global x
... print x
... x = 5
... print x
...
>>> f()
1
5
>>> x
5
How to treat the variable "x" inside the function as local without altering the global one when I have print statement above the variable assignment?
I expect the result of "x" to be 5 inside the function and the global x should be unaltered and remains the same in value (i.e) 1
I guess, there is no keyword called local in python contrary to global
>>> x = 1
>>> def f():
... print x
... global x
... x = 5
...
<stdin>:3: SyntaxWarning: name 'x' is used prior to global declaration
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.
Source.
It's true there's no local keyword in Python; instead, Python has this rule to decide which variables are local.
Any variable in your function is either local or global. It can't be local in one part of the function and global in another. If you have a local variable x, then the function can't access the global x. If you want a local variable while accessing the global x, you can call the local variable some other name.
The behaviour is already what you want. The presence of x = inside the function body makes x a local variable which entirely shadows the outer variable. You're merely trying to print it before you assign any value to it, which is causing an error. This would cause an error under any other circumstance too; you can't print what you didn't assign.

Must I always reference a variable as global in my function if I were to use it?

Firstly, it's related to this function:
a = 3
def b():
a = a+1
return a
returns an error.
Isn't it when the local environment of b couldn't find 'a', then it goes to the parent environment where b is defined, that is, where 'a' is found to be 3?
Why must I reference it with a global a in order for it to work? Meaning, do I have to do this only when the immediate parent environment is the global environment? Because the below function seems to be able to reference the parent environment:
def b():
def c():
print c
return c()
This is such a common issue that is is actually in the Python FAQ:
[...] when you make an assignment to a variable in a scope, that variable becomes local to that scope and shadows any similarly named variable in the outer scope. Since the last statement in foo assigns a new value to x, the compiler recognizes it as a local variable.
So, Python sees you're assigning to a local variable with a = .. and thinks, hey, a local variable, I'm fine with this. But, when it actually bumps into the content of that assignment (a + 1) and sees the same name a it complains because you're trying to reference it before assigning it.
This is why the following doesn't raise an error:
def b():
a = 20
a = a + 2
return a
You first make an assignment, a is treated as local to the function b and when a = a + 2 is encountered, Python knows a is a local variable, no confusion is present here.
In the other case you added:
def b():
def c():
print c
return c()
You are only referencing a name enclosed in the scope of function b, referencing. If you changed this to a similar assignment as before, you'll get the same UnboundLocalError:
def b():
def c():
c = c + 20
return c()
Because you are doing an assignment and the scope has to be clarified.
Your case:
a = 3
def b():
a = a + 1
return a
print(b())
Output:
Traceback (most recent call last):
File "./tests/test1.py", line 12, in <module>
print(b())
File "./tests/test1.py", line 9, in b
a = a + 1
UnboundLocalError: local variable 'a' referenced before assignment
When you do the assignement a = ... you have created always a local variable unless it has been declared global which is not the case. Local resolution takes precedence.
Your example would obviously work with:
a = 3
def b():
global a
a = a + 1
return a
print(b())
Which outputs:
4
But using globals, imho, should not be the way and hence passing a as a parameter to function b would be it:
a = 3
def b(a):
a = a + 1
return a
print(b(a))
Which agains produces the expected 4.
EDIT:
Following the edition in the OP: you can always "reference" the global scope (and parent scope). But if you make assignments they take place in the local scope.
If your code (which is wronly indented and contains a new c which isnt' anywhere) is meant to do this:
a = 3
def b():
def c():
print(a)
return c()
b()
It works ... and prints 3 because there is NO assignment and only referencing. As soon as you executing an assignment a would again be created in the local scope.
Assignment will create a new variable in Python.
Here may help you.

Python closure confusion

I was playing around with closures in Python and I do not understand why the following does not work and how to make it work:
>>> def make_counter():
... i = 0
... def inner():
... i += 1
... return i
... return inner
...
>>> c = make_counter()
>>> c()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in inner
UnboundLocalError: local variable 'i' referenced before assignment
Could anybody shed some light on it?
Thanks.
In the inner function, the statement
i += 1
can be understood like this
i = i + 1
since you are assigning a value to i in this function, Python thinks that you are creating a new variable scoped to this current function. And then when it executes the right hand side expression, i + 1, it finds that i has not been assigned any value before using it. That is why it is throwing
UnboundLocalError: local variable 'i' referenced before assignment
To fix this, you need to explicitly tell Python that you are not creating a new variable, instead, you are accessing a variable from the outer scope, with nonlocal (Python 3.x) like this
>>> def make_counter():
... i = 0
... def inner():
... nonlocal i
... i += 1
... return i
... return inner
...
>>>
>>> make_counter()()
1
Note: If you are using Python 2.x, follow any of the methods mentioned in this question

local variable referenced before assignment in python when i set it global

from random import randint
shifts = [4, 4.2, 5, 6, 7]
days_names = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday']
workers_names = ['Itai', 'Or', 'Reut', 'Kuka', 'Aviel']
counter = 1
def shift_arrange(worker):
for day in days.values():
counter+=1
global avilable_shifts
avilable_shifts = check_avilable_shifts(day)
if not random_shifte_selector(worker,day): soft_reset(worker)
I set counter as a global variable, and when i try to run this code i get the local variable error:
Traceback (most recent call last):
File "C:\Or\mypy\shift creator\shift cretor.py", line 144, in <module>
for w in workers.values(): shift_arrange(w)
File "C:\Or\mypy\shift creator\shift cretor.py", line 105, in shift_arrange
counter+=1
UnboundLocalError: local variable 'counter' referenced before assignmen
I saw some guy ask this question here, he deleted his pyc file or something(i don't know what is it) and its work fine. Why this is happen? Its not happen to other variables in the program.
Thanks, Or
You need to declare a global variable
def shift_arrange(worker):
global counter
for day in days.values():
counter+=1
...
Since you modify counter in that scope, python treats it as a local variable, unless you declare it as global. If you only need to read it, that isn't necessary.
Consider the following:
This works:
c = 0
def f():
print c
f()
While this does not:
c = 0
def f():
print c
c = 1
f()
While this does:
c = 0
def f():
global c
print c
c = 1
f()
print c # prints 1, f() modified the global value
There's a lot to look up here, but in general python has names, and things that are assigned to them. The name could be either in the local or global scope.
For reads, python looks through local scope, then through global scope, and uses the first one it finds (or error if it doesn't).
For writes... python needs to know where to put it. Normally, what it would do is look in the local scope, and if it's not there, create a variable there and assign the value. This would hide the global variable. You could have it also look in globals and use that one if it exists - but that could be undesirable & unexpected. So, you need a way to tell python to use a global variable instead if it exists (and then, it will create if it doesn't).
This leads to some odd behaviour sometimes as well. In addition to the earlier answer...
c = 0
# Passes. We are just looking up the global variable.
def f1(x):
return x + c
# Passes, but may not be as expected. Sets a local variable c to a value, does not
# modify the global one.
def f2(x):
c = x
# Correct way to do the above; now it sets the global variable.
def f3(x):
global c
c = x
# What if you mix them?
def f4(x):
c = c + x
# This fails. What happens is that first it sees that it's writing to c, and it's
# not marked global, so it assigns c to the local space. Then it tries to dereference
# it. Since we've marked it local, it masks the global one, and since it doesn't
# have a value, it throws an error. c += x works the same way and also fails.
# However, this works, though is just as equally wrong:
def f5(x):
d = c
c = d + x
# This one works, because we copy the value of the global c into the local d.
# Then, the second line assigns a local c to addition of local d and x.
# But does not update the global.
# Each line is separate though:
def f6(x):
d = c
c = c + 1
# You might think that d=c already made c local for the whole function. But it doesn't
# work like that. The first line just looks up the value for c, which it finds
# globally, but then forgets about it - it cares about the object c is the name of,
# not the name c itself. The second line still fails.

Why static binding works differently for class and function?

In python (tested on 2.7.6) all variables are
statically bound to a scope at compile time. This process is well
described in http://www.python.org/dev/peps/pep-0227/ and
http://docs.python.org/2.7/reference/executionmodel.html
It is explicitly stated that "If a name binding operation occurs
anywhere within a code block, all uses of the name within the block
are treated as references to the current block."
A function is a code block so the following code with fail because x
is assigned after its use (so at compile time it is defined local
because it is assigned somewhere in the function, but at execution
time, it is used before being bound).
x = 1
def f():
print x
x = 2
print x
>>> f()
Traceback (most recent call last):
File "<pyshell#46>", line 1, in <module>
f()
File "<pyshell#45>", line 2, in f
print x
UnboundLocalError: local variable 'x' referenced before assignment
A class is also a code block, so we should observe exactly the
same behavior. But this is not what I observe.
Look at this example:
x = 1
class C():
y = x + 10
x = 2
def __init__(self):
print C.y
>>> C.x
2
>>> C.y
11
>>> C()
11
<__main__.C instance at 0x00000000027CC9C8>
As the class definition is a code block, any assignment within this
block should make the variable local. So x should be local to the
class C, so y = x + 10 should result in an UnboundLocalError.
Why there is not such error?
Yes - it seems that the documentation is rather misleading. A class definition doesn't actually work quite the same as other normal blocks:
global_one = 0
class A(object):
x = global_one + 10
global_one = 100
y = global_one + 20
del global_one
z = global_one + 30
a = A()
print a.x, a.y, a.z, global_one
results in: 10, 120, 30, 0
if you try the same thing with a function, you get an UnboundLocalError on your first access of global_one.
The reason for this is that class definitions as normal have access to the parent scope, however, all name assignments do NOT modify a local scope, but in fact are captured into the class's data attributes dictionary. There are hints about this in the documentation, but it's certainly not obvious.

Categories