!! Scope

%%info
This is an additional exercise of the [MicroPython Tutorial|MicroPythonTutorial].
%%

When you type a __name__ its definition (and therefore its meaning and usage) as used by the Python interpreter 
''depends on where it is defined''.

In Python, a variable is only available from inside the region it is created. This is called __scope__. There are five different scopes:

* __local scope__: names defined inside a function (or lambda)
* __enclosing scope__: names in the local scope of enclosing functions (nested functions)
* __global scope__: names defined at the top level of a module (or declared global with the global keyword)
* __built-in__: reserved names preassigned by Python (e.g., {{import}}, {{def}}, {{for}})


[{TableOfContents title='Contents'}]

----

!! Local Scope

These are names defined inside a function or method.

%%filename local_scope.py %%
%%python {{{
# example of local scope

def local_example():
    # define a variable 'x' whose scope is local to the function
    x = "I'm local"
    print(x) 
    
# if we call local_example() the result will print "I'm local".
local_example()

# the following would result in an error, as x is not defined outside the function
print(x)
}}} %%


!! Enclosing Scope

These are names defined in the local scope of an enclosing function.

If you only ever expect to use a function within an existing function, yes, you can define 
a function within another function. This is called a __nested function__. The scope of names 
is therefore determined by where they are defined.

%%filename enclosing_scope.py %%
%%python {{{
# example of enclosing scope

def outer():

    # we define a variable 'x' within the scope of the outer() function
    x = "I'm in the enclosing scope"
    
    def inner():
        print(x)   # accesses x from enclosing function
    
    # if the outer() function is called, inner() is called too
    inner()
    
# we call the outer() function
outer()
}}} %%


!! Global Scope

These are names defined at the top level (i.e., starting at column 0) of a module, 
or declared global with the {{global}} keyword.

%%filename global_scope.py %%
%%python {{{
# example of global scope

x = "I'm global"

def show_global():
    print(x)        # read-only access to the global value of x
    
show_global()

}}} %%

If you want to be able to modify a global variable, you can use the {{global}} keyword. 

This is a safety feature so that you don't accidentally modify a global variable within a non-global scope unless you explicitly use the {{global}} keyword.

%%filename global_keyword.py %%
%%python {{{
# example of the global keyword

# we define a global variable called 'count'
count = 0

def increment_by_2():
    global count # we use the 'global' keyword so we can modify x
    count += 2

# we call the increment_by_2() function inside a for loop
for i in range(0,5):
    increment_by_2()
    print("i={}; count: {}".format(i, count))
}}} %%

When this code is executed, we can see the value of {{i}} being incremented by the {{for}} loop, as well as
the value of the global variable {{count}} being incremented within the {{increment_by_2()}} function.


%%repl {{{
/pyboard> repl
Entering REPL. Use Control-X to exit.
>
MicroPython v1.25.0 on 2025-04-15; Raspberry Pi Pico with RP2040
Type "help()" for more information.
>>> 
>>> import global_keyword
i=0; count: 2
i=1; count: 4
i=2; count: 6
i=3; count: 8
i=4; count: 10
>>> 
}}} %%