Why can functions in Python print variables in enclosing scope but cannot use them in assignment? [duplicate]

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>

Leave a Comment