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.
Enclosing (or nonlocal) scope is a special scope that only exists for nested functions. If the local scope is an inner or nested function, then the enclosing scope is the scope of the outer or enclosing function. This scope contains the names that you define in the enclosing function.
Enclosing Scope or Non-local Scope Enclosing scope is also known as non-local scope. They refer to the names of a variable defined in the nested function.
When the execution of the function terminates (returns), the local variables are destroyed. Codelens helps you visualize this because the local variables disappear after the function returns.
The LEGB rule is a kind of name lookup procedure, which determines the order in which Python looks up names. • For example, if we access a name, then Python will look that name up sequentially in the local, enclosing, global, and built-in scope.
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With