Scope in Python
When programming, we always at a point have to define variables and assign values to them.
Every variable we declare has a certain scope. The scope describes part of the program from which a variable can be accessed if required. In short, Scope refers to the visibility of variable and how they are searched for in a program
This guide explains scope, using python as a case study.
Local(i.e.Function) Scope:
A variable defined inside a function is accessible only inside the function. That means its scope(visibility) is only in the function.
def function_a(x):
print(x) #accessible
function_a(90) #invoke function
print(x) #not accessible
We defined a function called ‘function_a’. All variables present/declared inside the function are accessible inside the function only.
So the 1st print statement which sits inside function_a works fine because it’s able to see and access variable x while the second ‘print’
statement that is located outside of the function throws an error because variable x is inaccessible outside.
[*]Ouptput
90
- Enclosing(i.e.Nested Function) Scope:
A nested function is a function which is found in another function.
def outer_function():
name = 'Ray' #variable in outer function(i.e. enclosing function)
def inner_function():
print('Name from inner function::',name) #variable accessible in
inner_function()
In the code snippet up here, function ‘outer_function’ contains another function ‘inner_function’. Like we mentioned earlier, a variable defined inside a function is visible inside the function. Though inner function(nested function) is a function, it can still access variables defined in its ‘outer function’(Enclosing). Here a variable called ‘name’(declared in the outer_function(Enclosing)) can be accessed by the nested function since it’s inside the ‘outer function’. This is an enclosing scope. So the inner function can access variables in the enclosing function(i.e. outer_function)
[*]Usage
outer_function() #calling the function
[*]Output
Name from inner function:: Ray
Considering a modified version of the nested function again below:
def outer_function():
name = 'Ray' #variable in outer function
def inner_function():
age = 12 #variable in inner function
print(age) #calling age out of scope
We cleared a variable called age in the inner_function(), effort to access it outside of inner_function through **print(age)** throws an error likewise since it is out of it’s scope(function scope)
[*]Usage
outer_function()
[*]Output
NameError: name ‘age’ is not defined
[Note] It is important to clarify that inner_function cannot be called from outside of the outer_function since it’s declaration and scope exists inside of the outer_functionģ
[*]
def outer_function():
name = 'Ray' #variable in outer function
def inner_function():
print('Name from inner function::',name) #variable accessible in
inner_function()
inner_function() #this throws an error.
Global Scope:
A variable is said to have a global scope if it is defined outside of any function. Such kind of variable is accessible through out the whole program(i.e. in the file). A variable called name is accessible both in the defined function and class
name = "Python"
def show_name:
print("Name coming from function", name)
class Test:
def show(self):
print("Name coming from class", name)
#[*]Usage:
show_name() #calling a function
Test().show() #calling method
[*]Output
Name coming from function Python
Name coming from class Python
- Built-in Scope:
built-ins are the keywords provided by python. Even when they are not defined in the program. str,int,sum,print,range,etc. are all keywords that are available in a program. Running the command, print(dir(__builtins__)) reveals all the built-ins available.
print(dir(__builtins__))
[*]LEGB (Local->EnClosing->Global->Builtin)
When python is looking for any variable referenced in a program, it follows this order of scope:Local->Enclosing->Global->Builtin. If after
searching in this order it does not see where it is declared, it throws an error.
Take a look at this
#global scope area
def outer():
#enclosing/nested scope area
def inner():
#local/function scope area
print(x)
When the program is trying to execute print(x), the 1st place it looks is the local scope(inside the inner function). If it is not found,
it goes into outer() function, which is the enclosing scope. If not found, it goes to the global scope before finally looking for it in the built-in scope. Failure to find it leads to undefine variable error.
[*]Sample code
x = 10 #global
def outer():
#enclosing/nested scope area
def inner():
x = 90
#local/function scope area
print(x)
inner()
#[*]Usage
outer()
#[*]Output
90
The program makes use of the x = 90 because it looks at the local scope first. The output when x=90 is removed will be 10(making use of global since it is not found in both local and enclosing scope)
- global keyword:
We explained earlier that variables in the global scope are accessible from a function if the function does not declare them. Another question that needs to be answered is that can function modify variables in the global scope? Let’s consider an example.
[*]Snippet
global_variable = 10
def modify():
global_variable = 80
print("Value from modify function ",global_variable)
modify()
print("Value from global",global_variable)
[*]Output
Value from modify function: 80
Value from global: 10
We tried to modify variable global_variable which is in global scope but it turns out the the value didn’t change. What has happened? printing out global_variable inside the function shows it change. But why didn’t it reflect on the global scope?
What happened was that python created a new local variable “global_variable” in the modify() function scope because it cannot it’s not possible to modify a global variable directly from function scope.
To escape this behavior we need to tell python to use the global scope variable instead of creating a new one. The global keyword works in this regard.
[*]global keyword
global_variable = 10
def modify():
global global_variable
global_variable = 80
print("Value from modify function ",global_variable)
modify()
print("Value from global",global_variable)
[*]Output
Value from modify function: 80
Value from global: 80
Great! This modifies the variable in the global scopes.With the global keyword, you are telling python not to create a local variable. You tell to Python that this variable in the function scope should map to the one in the global scope
[*] Second example
initial_value = 9 #glocal scope
def increment():
initial_value = initial_value + 1
# [*]usage
increment()
[*]Output
UnboundLocalError: local variable ‘initial_value’ referenced before assignment
We got an error that initial_value is not reference. Remember that we have established earlier that it is impossible to update a global variable from inside a function. So Python created a variable ‘initial_value’ in the local(function) scope. While it was trying to execute the other part of the code (initial_value + 1), it encounter a variable initial_value again so it assumes that it is a variable in the function.
The workaround is to tell Python that the initial value in the global scope should be used and not to create a new one.
initial_value = 9 #glocal scope
def increment():
global initial_value # hey, use the initial_value in global scope,
# dont create a new initial_value in function scope
initial_value = initial_value + 1
[*]nonlocal keyword.
Similar to how global scope works, variables in the outer function of a nested function can be accessed from the inner function(i.e. enclosing scope) but cannot be modified.
[*]
def outer():
#enclosing scope
x = 10
def inner():
#local scope
x = 20
print("coming from inner", x)
inner()
print("coming from outer", x)
#[*]Usage
outer()
# [*]Output
coming from inner 20
coming from outer 10
We modified the value of x from the inner function(local scope) but didn’t change the value. With the use of nonlocal we can list variable names that won’t be treated as nonlocal. It makes the name refer to variable in the closest enclosing scope(outer function in our case).
def outer():
#enclosing scope
x = 10
def inner():
#local scope
nonlocal x
x = 20
print("coming from inner", x)
inner()
print("coming from outer", x)
# [*]Usage
outer()
# [*]Output
coming from inner 20
coming from outer 20
We have been able to modify the variable
[*]Note:The nonlocal keyword can only be used in an enclosed/nested
Ready to elevate 🚀 your Python 🐍 skills? Check out YouTube for exclusive content. Consider subscribing to stay updated.
Thanks for reading and feel free to explore my video collection on YouTube for more educational content. Don’t forget to subscribe to the channel to stay updated with future releases.