# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# <small><small><i>
# All the IPython Notebooks in **Python Functions** lecture series by Dr. <NAME> are available @ **[GitHub](https://github.com/milaan9/04_Python_Functions)**
# </i></small></small>

# # Python Global, Local and Nonlocal variables
#
# In this class, you’ll learn about Python Global variables, Local variables, Nonlocal variables and where to use them.
#
# When we define a function with variables, then those variables scope is limited to that function. In Python, the scope of a variable is the portion of a program where the variable is declared. Parameters and variables defined inside a function are not visible from outside the function. Hence, it is called the variable’s local scope.
#
# >**Note:** The inner function does have access to the outer function’s local scope.
#
# When we are executing a function, the life of the variables is up to running time. Once we return from the function, those variables get destroyed. So function does no need to remember the value of a variable from its previous call.

# ## Global Variables
#
# In Python, a variable declared outside of the function or in global scope is known as a global variable. This means that a global variable can be accessed inside or outside of the function.
#
# For example:

# +
# Example 1: Create a Global Variable

global_var = 999

def fun1():
    print("Value in 1st function:", global_var)

def fun2():
    print("Value in 2nd function:", global_var)

fun1()
fun2()

# +
# Example 2: 

x = "global"

def fun():
    print("x inside:", x)

fun()
print("x outside:", x)
# -

# In the above code, we created **`x`** as a global variable and defined a **`fun()`** to print the global variable **`x`**. Finally, we call the **`fun()`** which will print the value of **`x`**.
#
# What if you want to change the value of **`x`** inside a function?

# +
# Example 3: 

x = "global"

def fun():
    x = x * 2
    print(x)

fun()
# -

# **Explanation:**
#
# The output shows an error because Python treats **`x`** as a local variable and **`x`** is also not defined inside **`fun()`**.
#
# To make this work, we use the **`global`** keyword. Visit **[Python Global Keyword](https://github.com/milaan9/04_Python_Functions/blob/main/003_Python_Function_global_Keywords.ipynb)** to learn more.

# +
# Example 4: 

global_lang = 'DataScience'

def var_scope_test():
    local_lang = 'Python'
    print(local_lang)

var_scope_test()  # Output 'Python'

# outside of function
print(global_lang)   # Output 'DataScience'

print(local_lang)   # NameError: name 'local_lang' is not defined

# +
# Example 5: 

a=90   # 'a' is a variable defined outside of function, i.e., Global variable

def print_data():
    a=6  # 'a' is a variable defined inside of function, i.e., local variable
    b=30
    print("(a,b):(",a,",",b,")")

print_data() #(a,b):( 5 , 10 )
print("Global a :",a)  #Global x : 50
print("Local b : ",b)  #b is local veriable - throw NameError


# -

# ## Local Variables
#
# A variable declared inside the function's body or in the local scope is known as a local variable.
#
# If we try to access the local variable from the outside of the function, we will get the error as **`NameError`**.

# +
# Example 1: Accessing local variable outside the scope

def fun():
    y = "local"
    
fun()
print(y)


# -

# The output shows an error because we are trying to access a local variable **`y`** in a global scope whereas the local variable only works inside **`fun()`** or local scope.

# +
# Example 2: Create a Local Variable

# Normally, we declare a variable inside the function to create a local variable.

def fun():
    y = "local"
    print(y)

fun()


# -

# Let's take a look at the cell **In [2]: # Example 3:** where **`x`** was a global variable and we wanted to modify **`x`** inside **`fun()`**.

# +
# Exercise 3: 

def fun1():
    loc_var = 999   # local variable
    print("Value is :", loc_var)

def fun2():
    print("Value is :", loc_var)

fun1()
fun2()
# -

# ## Global and local variables
#
# Here, we will show how to use global variables and local variables in the same code.

# +
# Example 1: Using Global and Local variables in the same code

x = "global"

def fun():
    global x
    y = "local"
    x = x * 2
    print(x)
    print(y)

fun()
# -

# **Explanation**:
#
# In the above code, we declare **`x`** as a global and **`y`** as a local variable in the **`fun()`**. Then, we use multiplication operator **`*`** to modify the global variable **`x`** and we print both **`x`** and **`y`**.
#
# After calling the **`fun()`**, the value of **`x`** becomes **`global global`** because we used the **`x * 2`** to print two times **`global`**. After that, we print the value of local variable **`y`** i.e **`local`**.

# +
# Example 2: Global variable and Local variable with same name

x = 9

def fun():
    x = 19
    print("local x:", x)


fun()
print("global x:", x)


# -

# **Explanation**:
#
# In the above code, we used the same name **`x`** for both global variable and local variable. We get a different result when we print the same variable because the variable is declared in both scopes, i.e. the local scope inside **`fun()`** and global scope outside **`fun()`**.
#
# When we print the variable inside **`fun()`** it outputs **`local x: 19`**. This is called the local scope of the variable.
#
# Similarly, when we print the variable outside the **`fun()`**, it outputs **`global x: 9`**. This is called the global scope of the variable.

# +
# Exercise 3: 

def my_func():  # for this Function I am not writing any argument in parenthesis '()'
    x = 10
    print("Value inside the body of function:",x)

x = 20                             # first, this line to execute
my_func()                          # second, the body of function will execute
print("Value outside of function:",x) # finally, this line will execute
# -

# **Explanation:**
#
# Here, we can see that the value of **`x`** is 20 initially. Even though the function **`my_func()`** changed the value of **`x`** to 10, it did not affect the value outside the function.
#
# This is because the variable **`x`** inside the function is different (local to the function) from the one outside. Although they have the same names, they are two different variables with different scopes.
#
# On the other hand, variables outside of the function are visible from inside. They have a global scope.
#
# We can read these values from inside the function but cannot change (write) them. In order to modify the value of variables outside the function, they must be declared as global variables using the keyword global.

# ## Nonlocal Variables
#
# Nonlocal variables are used in nested functions whose local scope is not defined. This means that the variable can be neither in the local nor the global scope.
#
# Let's see an example of how a global variable is created in Python.
#
# We use **`nonlocal`** keywords to create nonlocal variables.

# +
# Example 1: Create a nonlocal variable

x1 = "global"  # Global variable

def outer_fun():  # main function
    x1 = "local"  # 'x' is local variable for main function and it is nested variable for nested function
    print("variable type for Outer function:", x1)
    
    def inner_fun():  # nested fucntion
        nonlocal x1   # using local variable 'x' in nested function as nonloval variable
        x1 = "nonlocal"  # changing the value of my 'x'
        print("variable type for Inner function:", x1)  # print 'nonlocal'

    inner_fun()   #print("outer:", x1)  # print 'nonlocal'
    
outer_fun()
print("Variable type of x1:", x1)


# -

# In the above code, there is a nested **`inner()`** function. We use nonlocal keywords to create a **`nonlocal`** variable. The **`inner()`** function is defined in the scope of another function **`outer()`**.
#
# > **Note**: If we change the value of a nonlocal variable, the changes appear in the local variable.

# +
# Exercise 2: 

def outer_fun():
    x = 999

    def inner_fun():
        # local variable now acts as global variable
        nonlocal x
        x = 900
        print("value of x inside inner function is:", x)

    inner_fun()
    print("value of x inside outer function is:", x)

outer_fun()
# -



StarcoderdataJupyterScriptsDedupFiltered