Python Scope

Efficient and ultimate rule for python scope | LEGB | 8 mins read

Holla, today we are going to talk about "Python Scope & LEGB Rule". The scope refer to the area,region or environment in which an entity can refer to some value or other entity.LEGB stands for Local,Enclosing,Global and BuiltIn scope. We can modify the scope of a variable using keywords like global and nonlocal.

Table of Contents

Introduction

Holla, Welcome back to another exciting tutorial on “Python Scope & LEGB rule”. In this Python tutorial, you’ll learn the concept of scope, why python scope is necessary, visibility of variables in the code , the python scope concept also defines as LEGB rule.

In the end, you learn how to modify the standard behavior of Python scope using global and nonlocal. Let’s begin our tutorial with an introduction to the Scope.

If you aren’t following along, I would recommend you to go back where you left or start from here.

What is a Scope ?

Entity here refers to the identifiers of variables,objects,functions,classes or any other object that can be assigned a name.
The scope refer to the area,region or environment in which an entity can refer to some value or other entity.The “Scope” is also known as visibility of an entity i.e. an entity outside its scope will not exist.

Why Scope is necessary ?

The problems face by the programmer if there is no scope i.e. only global scope are :-
  • Name Collisions – While writing large programs , there are chances that you declare two variables with same name. If there are no such things like scope the later declaration of variable will override the first one . In practical case , you don’t want these things to happen so the concept of scope is introduced .
  • Modular Programming – In modular programming , scope rules are important else change in one part of the code might break the unrelated part of the code.
The most common type of scope are:-
  1. Global Scope :- The entity which you define in this scope can be accessed from any part of the program.
  2. Local Scope :- The entity which you defines here is available only to code which is present in the scope.
In this tutorial, we are going to talk about Python scope in detail.If you want to learn more about Scope in Computer Science, you can check here.

Python Scope

Before introducing the concept of scope in python , I would like to talk about the term namespace as both are closely related to each other.

NamespaceIt is an object that holds a set of names . In python , the object is dictionary . You can access the names by listing the keys of dictionary. When you create a new entity it is stored in the namespace depending on where you create the entity in the program.

Namespaces support modular programming by preventing naming conflicts.

Example –

>>> import os
>>> os.__dict__.keys()
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__file__', '__cached__', '__builtins__', 'abc', 'sys', 'st', '__all__', '_exists', '_get_exports_list', 'name', 'linesep', 'stat', 'access', 'chdir', 'chmod', 'getcwd', 'getcwdb', 'link', 'listdir', 'lstat', 'mkdir', 'readlink', 'rename', 'replace', 'rmdir', 'symlink', 'system', 'umask' .....truncate .... ])
>>> 

In the above code, the list of all names is the namespace of os module.

Namespace in Python
Namespace in Python

How Namespace contribute in Python Scope Strategy ?

>>> import os
>>> getcwd = "This doesn't override the member(getcwd) of os as it has different namespace"
>>> print(os.getcwd())     #Current working directory
'C:\\Users\\XYZ'
>>> print(getcwd)
"This doesn't override the member(getcwd) of os as it has different namespace"

Explanation – In the above code , we assign a string to a variable which has same name as one of the member of the os module .Voila , No error occurred . The python differentiates in both with the help of namespace. os.getcwd() is present in os module namespace and getcwd is present in global namespace of current program.

Local,Enclosing,Global or builtin scope has their own namespace . It works as a lookup table to check if a variable is present or not or two variables with same name are different or same.

Namespace is a dictionary that provides python an easy way to check if an entity is present in any scope or not present .

What is LEGB Rule ?

Python differentiates between two entity using this rule. The LEGB stand for local,enclosing,global and builtin scope.

LEGB

The LEGB rule works in the same way as these letter stands . The python interpreter searches for an entity first in local scope —-> enclosing scope —-> global scope —-> built-in scope or not found.

Local (Function) Scope

It is the piece of code under the body of python function or lambda expression.
The entity declared here are accessible from the code of the function.The scope is created at the time of function call not at the time of function declaration.

When the function call completes , the local scope for that call is destroyed and the entities are deleted . Let us now look how this happen.

>>> def topictrick():
...     x = 'I am local'
...     print(x)
...
>>> topictrick()
I am local

Case 1

>>> print(x)
Traceback (most recent call last):
File "", line 1, in 
NameError: name 'x' is not defined
>>>

Series Of Events :- Python checks for these queries in the same way as listed and stop when query is fulfilled.

  • Is x in local scope – NO
  • Is x in enclosing scope – NO
  • Is x in global scope – NO
  • Is x in built-in scope – NO

Hence , x is not found .Show error


You can use __code__ which is an magic attribute of a function to get information about the code in function body.

>>> topictrick.__code__.co_varnames
('x',)          # Tuple containing names of entities inside the function
>>> topictrick.__code__.co_argcount
0               # Number of arguments
>>>

Enclosing (nonlocal) Scope

When we declare nested function , the local scope of enclosing function is termed as enclosing scope for nested function . The entities declared in enclosing scope are known as nonlocal names.
The entity present in the scope of enclosing function are visible to nested function but not vice-versa.

Enclosing Space
Enclosing Space

Let us look over some python code to understand better.

Case 1

>>> def outer_func():
...     x = 'outer x variable'
...     def inner_func():
...             x = 'inner x variable'
...             print(x)
...     inner_func()
...
>>> outer_func()
inner x variable

Series Of Events :- We calls  outer_func  and outer_func call inner_func , inner_func executes print x statement . Hence, python search for x as:

  • Is x in local scope – YES

Hence , x is found containing string ‘inner x variable’ .

NOTE : The x variable in enclosing scope is not  override by x in local scope.


Case 2

>>> def outer_func():
...     x = 'outer x variable'
...     def inner_func():
...             print(x)
...     inner_func()
...
>>> outer_func()
outer x variable

Series Of Events :- We call outer_func and outer_func call inner_func , inner_func executes print x statement . Hence, python search for x as:

  • Is x in local scope – NO
  • Is x in enclosing scope – YES

Hence , x is found containing string ‘outer x variable’ .


Case 3

>>> def outer_func():
...     def inner_func():
...             x = 'inner x variable'
...     print(x)
...
>>> outer_func()
Traceback (most recent call last):
File "", line 1, in 
File "", line 4, in outer_func
NameError: name 'x' is not defined

Series Of Events :- We call outer_func and it executes print x statement . Hence, python search for x as:

  • Is x in local scope – NO
  • Is x in enclosing scope – NO
  • Is x in global scope – NO
  • Is x in built-in scope – NO

Hence , x is not found.Show error.

NOTE : Inner function can access the outer function names but not vice versa.


Global (Module) Scope

It is the outermost scope in the python program or module .The entities in this scope can be accessed from every part of the code.

Case 1

>>> x = 'global x'
>>> def func1():
...     print(x)
...
>>> func1()
global x
>>>

Series Of Events :- We call func1 and it executes print x statement . Hence, python search for x as:

  • Is x in local scope – NO
  • Is x in enclosing scope – NO
  • Is x in global scope – YES

Hence , x is found containing string ‘global x’.


Built-in Scope

When you open a new python session , built in names such as keywords , functions , exceptions are loaded . These names are accessible from every part of the code.

Let us look on a piece of code that determines the built in scope for the python.

We are currently working on Python 3.8.1 so the output of the program may differ on your system.

Program
Previous slide
Next slide

Case 1

>>> list1 = [1,3,4,6,7,9,8]
>>> l = len(list1)
>>> print(l)
7
>>>

Series Of Events :- We call len function. Hence, python search for len as:

  • Is len in local scope – NO
  • Is len in enclosing scope – NO
  • Is len in global scope – NO
  • Is len in builtin scope – YES

Hence , len function is executed with list1 as argument.


Case 2

Let us consider you create a set function to set the last bit of a number .

>>> def set(num):
...     num = num | 1
...     return num
...
>>> set1 = set()         # Create an empty set 
Traceback (most recent call last):
File "", line 1, in 
TypeError: set() missing 1 required positional argument: 'num'

Series Of Events :- We call set function. Hence, python search for set as:

  • Is set in local scope – NO
  • Is set in enclosing scope – NO
  • Is set in global scope – YES

The python found set in global scope .So it terminates its search there and not go for the built in set method as you expected .Hence , Error shown

Always verify the entity you declared in python module does not match with anyone in built-in scope.


Modifying the Python Scope

Till here , you have learned how python search for a name in different namespace and how an entity is accessed or visible within a certain area i.e scope .

You have seen that an entity in the global scope can be accessed from any part of the code , but can’t be modified or updated from anywhere.

Similarly , a nonlocal entity can be accessed by the nested function but it cannot be modified from there.

In real world scenario, there are chances that a programmer need to disobey the general rules of the python scope .Python provides some keywords to modify the general scoping rules. The keywords are:-

  1. global
  2. nonlocal

The global Keyword

The global keyword is used to map or declare an entity to the global scope from non-global scope.

Syntax:- global var1,var2,…

Why global keyword is necessary ?

>>> x = 1
>>> def inc():
...     x+=1         # This line causes error
...     print(x)
...
>>> inc()
Traceback (most recent call last):
File "", line 1, in 
File "", line 2, in inc
UnboundLocalError: local variable 'x' referenced before assignment
>>>

Behind the scene : – When you call the inc function , a local scope is created for inc() . When you assign x = x + 1, python assumes that x should be in the local scope .But it is not present in the local scope .Hence , UnboundLocalError is shown.

Case 1 :- Updating a global variable in Local Scope.

>>> x = 1
>>> def inc(): ... global x #Referencing the global x variable ... x = x + 1 ... print(x) ... >>> inc() 2 >>> print(x) #Verify if change takes place 2 >>>

Behind the scene : – When you use global keyword with x variable ,it maps the variable to the global scope . Now , the change in x variable within the function will reflect in global x variable.

Case 2 :- Creating a global variable in Local Scope.

>>> def create_global():
...     global x
...     x = 'I am global variable created in local scope'
...
>>> create_global()
>>> print(x)         #Verification
I am global variable created in local scope
>>>

Behind the scene : – When you use global keyword with a variable ,it maps the variable to the global scope . Hence , x variable in create_variable() function is mapped to the global scope. Hence , the print(x) statement shows NO ERROR.


The nonlocal Keyword

The nonlocal keyword is used to treat an entity(or entities) as nonlocal.

You cannot use nonlocal statement other than a nested function.

>>> nonlocal x
File "", line 1
SyntaxError: nonlocal declaration not allowed at module level
>>> def func():
...     nonlocal x
...
...
File "", line 2
SyntaxError: no binding for nonlocal 'x' found
>>>

Syntax:- nonlocal var1,var2,…

Why nonlocal keyword is necessary ?

>>> def out_fun():
...     x = 20
...     def inc_out():
...             x = x + 1
...             print(x)
...     inc_out()
...
>>> out_fun()
Traceback (most recent call last):
File "", line 1, in 
File "", line 6, in out_fun
File "", line 4, in inc_out
UnboundLocalError: local variable 'x' referenced before assignment
>>>

Behind the scene : – When you call the out_fun function , out_fun calls inc_out function so a local scope is created for inc_out() . When you assign x = x + 1, python assumes that x should be in the local scope .But it is not present in the local scope .Hence , UnboundLocalError is shown.

Case 1 :- Updating a nonlocal variable in Local Scope.
>>> def out_fun():
...     x = 20
...     def inc_out():
...             nonlocal x
...             x = x + 1
...             print(x)
...     inc_out()
...
>>> out_fun()
21
>>>
Behind the scene : – When you use nonlocal keyword with x variable ,it maps the variable to the nonlocal scope. Now , the change in x variable within the function will reflect in nonlocal x variable.

Unlike global keyword, you cannot use nonlocal keyword to create nonlocal entity from local scope.

>>> def out_fun():
...     def create_x():
...             nonlocal x
...             x = 'I am nonlocal'
...     create_x()
...     print(x)
...
File "", line 3
SyntaxError: no binding for nonlocal 'x' found
>>>

Cheat Sheet

Operation Local Code Enclosed Function Code Global Code

Access entities of Local Scope

(*Same Local Scope)

Modify entities of Local Scope

(*Same Local Scope)

Access entities of Enclosing Scope

(*If declared as nonlocal else No)

Modify entities of Enclosing Scope

(*If declared as nonlocal else No)

Access entities of Global Scope

Modify entities of Global Scope

(*If declared as global else No)

(*If declared as global else No)

(*If declared as global else No)

Access entities of Built-in Scope

Override entities of Built-in Scope

(*During function call)

(*During function call)

Conclusion

If you are reading this , I want you to know that you have made a very clear understanding of scope and namespaces in python and their use . Now, you can answer any query regarding python scope and LEGB rule with an ease . In the next tutorial, we will talk about some new stuff regarding python .

Last but not the least, do leave your feedback and suggestions. We would love to hear your feedback.

Till then , keep coding !