Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

python

scope

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.

like image 440
Shashank Avatar asked Sep 18 '13 04:09

Shashank


People also ask

What does enclosing scope mean in Python?

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.

What is enclosed variable in Python?

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.

What happens to variables in a local scope when the function call returns Python?

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.

What is LEGB rule in Python?

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.


1 Answers

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> 
like image 126
Ashwini Chaudhary Avatar answered Sep 17 '22 16:09

Ashwini Chaudhary