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.
From Python scopes and namespaces:
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.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.
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:
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 aglobal
statement alerts code readers to that a function may be rebinding a global name.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 thex
.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 aglobal
statement, but none is found.Therefore,
x
is local, but it is not bound to anything, and yet it is used in the expressionx + 1
. ThrowUnboundLocalError
.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.