This question already has answers here:
Assigning to variable from parent function: "Local variable referenced before assignment" [duplicate]
(5 answers)
Short description of the scoping rules?
(9 answers)
Closed 8 years ago.
I come from a land of heavy Java and PHP experience so when it comes to Python much of the rules do not make sense to me.
I have a recursive Fibonacci function below that spits out the error:
Traceback (most recent call last):
File "C:\Users\Nic\workspace\lab8\src\Main.py", line 26, in <module>
print fibcount(27),"took",calls,"calls."
File "C:\Users\Nic\workspace\lab8\src\Main.py", line 19, in fibcount
calls += 1
UnboundLocalError: local variable 'calls' referenced before assignment
Here is my code:
calls = 0
def fibcount(n):
calls += 1
if n < 2:
return (1,1)
f1,c1 = fibcount(n-1)
f2,c2 = fibcount(n-2)
return (f1+f2,c1+c2+1)
print fibcount(27),"took",calls,"calls."
In Java this would obviously work because calls is a global variable in respect to the function fibcount() so it confuses me that calls is somehow not in scope.
What am I doing wrong?
In Python, you need to declare global variables as being global inside functions which reassign them to a different value. You would use global for this:
def fibcount(n):
global calls
...
Otherwise, Python will treat the calls variable as if it were local.
Note however that you only need to do this for global variables which you reassign. Simply reading their values will work fine.
Also, reassigning global variables like this is considered ugly in Python and should be avoided whenever possible. Most of the time, you should stick to passing values to functions and then reassigning variables to their return values. If you need to maintain state, use a class.
The only time global variables are warranted in Python is when you want to have module-level constants. And in that case, the convention is that they should have all-caps names:
MYCONST = 12345
Related
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)
Closed 5 months ago.
If we run this code
a = 1
def foo():
b = a + 2
print(b)
foo()
it works.
But if we run this code
a = 1
def foo():
b = a + 2
print(b)
a = a + 4
print(a)
foo()
it doesn't work.
Question:
why in the first example b = a + 2 works without errors but in the second example the same line of code b = a + 2 is broken?
why if there is no reassignment of a we can get a from a global scope but if there is a reassignment we don't have an access to a even if new local a doesn't even exist yet (because the reassignment isn't finished).
Question: Why [...] the first example works without errors but in the second example the same line of code is broken?
Because in the first example, you're only reading from a global a, then writing to a local b.
In the second example, a is assumed to be a local too (since you're not declaring global a), and it has no value when you're trying to read it in the first line, hence an error.
This is similar to JavaScript's Temporal Dead Zone, if that's familiar.
As for the question in your title:
Why it's impossible to reassign global name within local scope (without global keyword) with using the global name's value in reassignment?
You're not reassigning
a global name at all in either of your examples, since indeed you're not using the global keyword. In Python, you can always read a global (or "ambient") name; otherwise it'd be a pain to e.g. use any builtin function (since they're not locals), and having a special exception for builtins would be, well, a special exception, and those aren't nice.
This question already has answers here:
How to get local variables updated, when using the `exec` call?
(3 answers)
Closed 1 year ago.
Given a string (e.g. "lista=[1,2,3]") I would like to be able to use the variable lista.
exec() does the work outside a function, but when used inside a function the variables cannot be used in that same function. I guess it has something to do with local and global variables but I don't really understand the problem.
For example,
def funcion(texto):
exec(texto)
print(lista)
funcion("lista = [3,4,5]")
Gives the error: NameError: name 'lista' is not defined.
add globals
def funcion(texto):
exec(texto, globals())
print(lista)
funcion("lista = [3,4,5]")
This question already has answers here:
Local (?) variable referenced before assignment [duplicate]
(3 answers)
Closed 4 years ago.
I have some code from a beginner's coding exercise:
numbers = []
i = 0
def populate(maximum, step):
while i < maximum:
numbers.append(i)
i = i + step
populate(10, 2)
Which fails with the stack trace:
Traceback (most recent call last):
File "test_python.py", line 9, in <module>
populate(10, 2)
File "test_python.py", line 5, in populate
while i < maximum:
UnboundLocalError: local variable 'i' referenced before assignment
Here is my understanding of the problem so far...
i is an int and therefore it is immutable, and numbers is a list and is mutable
Since i is immutable, it can be read while it is out of scope. However, overwritting it while it is not in scope (ie. in the populate function), causes an UnboundLocalError
Since numbers is a mutable list, appending to it does not cause an overwrite and therefore does not cause an UnboundLocalError
If a simple change is made to the populate method, the program works successfully (due to i not being overwritten)
def populate(maximum, step):
new_i = i
while new_i < maximum:
numbers.append(i)
new_i = new_i + step
If I comment out the i = i + 1 line, the while loop (obviously) runs forever, but the program does not fail.
My question is then, why does Python fail when it hits the while loop on line 5, instead of the actual problem on line 7 (i = i + 1)? Is this some artifact of the interpreter taking the while loop as a block of code?
This piece of code fails in the correct spot:
def populate(maximum, step):
while i < maximum:
raise Exception("foo")
Stack trace:
Traceback (most recent call last):
File "test_python.py", line 12, in <module>
populate(10, 2)
File "test_python.py", line 6, in populate
raise Exception("foo")
Exception: foo
Another note: This only seems to be the case when the variable is used within the start of the control block (ie. while i < maximum). And the same behavior occurs for every type of control block: while, for, if, elif, etc.
Mutability is a red herring here. Mutable values are affected by scope the same way as immutable values. In fact, nothing in the Python language handles mutable values specially (this is a common myth, however).
The key insight is that the scope of a name is fixed in each scope. Within the scope of populate, every name must either be local or global: this decision is a part of the bytecode of the method.
A name that is only read can be looked up from an enclosing scope. But an assignment to a name anywhere within a scope forces that to be treated as a local variable everywhere in the scope. (Unless you use the global or nonlocal keywords.)
So if you're assigning to i anywhere in the method, then i must be a new variable local to the method, and not the global i. If you want i to mean the global i, simply add this line at the top of the method:
global i
This question already has answers here:
Short description of the scoping rules?
(9 answers)
Closed 5 years ago.
I was thinking that variable can be only accessed after its declaration.
But apparently, Python's name resolution starts to look for from inside to outer.
My question is that this is bad practice in terms of readability? I was wondering that this might be a common knowledge for Pythonista so that I can write this kind of code from now on.
def outer():
def inner():
print x
x = ‘foo’
inner()
>>> outer()
>>> ‘foo’
When you invoke inner(), it starts executing. On line print x it sees variable name x. Then it looks to inner scope of function inner, doesn't find variable x there. Then it looks to the outer scope, namely, scope of outer. At this moment there is already variable x defined (as it is done before inner() invokation) and this variable is used.
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)
Why does code like `str = str(...)` cause a TypeError, but only the second time?
(20 answers)
Closed 6 months ago.
Could anyone explain the exception the below code. It only works when I change the var sub in the display() to another name. There is no global variable sub as well. So what happened ?
def sub(a, b):
return a - b
def display():
sub = sub(2,1) // if change to sub1 or sth different to sub, it works
print sub
Any variable you assign to inside a scope is treated as a local variable (unless you declare it global, or, in python3, nonlocal), which means it is not looked up in the surrounding scopes.
A simplified example with the same error:
def a(): pass
def b(): a = a()
Now, consider the different scopes involved here:
The global namespace contains a and b.
The function a contains no local variables.
The function b contains an assignment to a - this means it is interpreted as a local variable and shadows the function a from the outer scope (in this case, the global scope). As a has not been defined inside of b before the call, it is an unbound local variable, hence the UnboundLocalError. This is exactly the same as if you had written this:
def b(): x = x()
The solution to this is simple: choose a different name for the result of the sub call.
It is important to note that the order of use and assignment makes no difference - the error would have still happened if you wrote the function like this:
def display():
value = sub(2,1) #UnboundLocalError here...
print value
sub = "someOtherValue" #because you assign a variable named `sub` here
This is because the list of local variables is generated when the python interpreter creates the function object.
This was originally a comment. The OP found this useful as an answer. Therefore, I am re-posting it as an answer
Initially, sub is a function. Then, it becomes the return value of a function. So when you say print sub, python doesn't know which sub you are referring to.
Edit:
First you define a function sub. Now, python knows what sub is.
When you create a variable and try to assign to it (say x = 2), python evaluates the stuff on the right hand side of the = and assigns the value of the evaluation as the value of the stuff on the left hand side of the =. Thus, everything on the right hand side should actually compute.
So if your statement was x = x+1, then x better have a value assigned to it before that line; and the previously defined x has to be of some type compatible with the addition of 1.
But suppose x is a function, and you make a variable called x in some other function, and try to assign to it, a value computed with function x, then this really starts to confuse python about which x you are referring to. This is really an oversimplification of this answer, which does a much better job of explaining variable scope and shadowing in python functions
For every variable used, Python determines whether it is a local or a nonlocal variable. Referencing a unknown variable marks it as nonlocal. Reusing the same name as a local variable later is considered a programmers mistake.
Consider this example:
def err():
print x # this line references x
x = 3 # this line creates a local variable x
err()
This gives you
Traceback (most recent call last):
File "asd.py", line 5, in <module>
err()
File "asd.py", line 2, in err
print x # this line references x
UnboundLocalError: local variable 'x' referenced before assignment
What happens is basically that Python keeps track of all references to names in code. When it reads the line print x Python knows that x is a variable from a outer scope (upvalue or global). However, in x = 3 the x is used as a local variable. As this is a inconsistency in the code, Python raises an UnboundLocalError to get the Programmers attention.
Python start executing your code and get the function first
def sub(a, b):
return a - b
So after executing this interpreter get the sub as a function. Now when come to next line it found
def display():
sub = sub(2,1) // if change to sub1 or sth different to sub, it works
print sub
so first line sub = sub (2, 1) will convert the sub function to sub variable. From this function you are returning the sub variable. So its create problem.