4: Scope, Lifetime, and Closure

Summary

  • A variable that a function assigns to is created as a local variable

  • To reference a global variable that a a function assigns to, it has to be declared as global

  • Functions can be defined within other functions and the inner functions have access to the containing functions local variables, but not vice versa

  • if an inner functions assigns to a variable then it is created as a local variable

  • To refer to a local variable of a containing function in an assignment you have to declare the variable nonlocal

  • Closure is a natural consequence of function objects outliving their local variables
    • If an inner function exists and can be invoked after its containing function has ended, then it still has access to the same local variables via the closure

  • The closure consists of all of the variables in scope when a function is declared

  • All functions declared within the same execution context share that context as their closure

  • The values of variables in the closure are the last values they had before the outer function terminated

  • Closures has many uses, but the main ones are to provide private state variables, provide a self-reference, and to provide context to callback functions

  • You can work with the “__closure__” magic attribute to access, and even change, the closure, but how this works is platform-dependent.

Program

# Global variable
counter = 100

def outer_function(message):
    # Local variable to outer_function
    outer_var = "Outer Variable"

    print("Inside outer_function:")
    print("Local outer_var:", outer_var)
    print("Accessing global counter:", counter)
    print()

    def inner_function():
        # Local variable to inner_function
        inner_var = "Inner Variable"

        # Using nonlocal to modify outer_function's variable
        nonlocal outer_var
        outer_var = "Outer Variable Modified by Inner"

        print("Inside inner_function:")
        print("Local inner_var:", inner_var)
        print("Modified outer_var:", outer_var)
        print("Accessing global counter:", counter)
        print("Message from outer_function:", message)
        print()

    return inner_function  # Returning inner function (closure)


# Create closure
closure_example = outer_function("Hello from closure!")

# Call the returned inner function
closure_example()

print("Back in global scope:")
print("Global counter:", counter)

Program Output

(docs-env) root@BMitchellLTOP:~/git/sphinx_students/source/programming_lang/book/programs# python3 chapterFour.py
 Inside outer_function:
 Local outer_var: Outer Variable
 Accessing global counter: 100

 Inside inner_function:
 Local inner_var: Inner Variable
 Modified outer_var: Outer Variable Modified by Inner
 Accessing global counter: 100
 Message from outer_function: Hello from closure!

 Back in global scope:
 Global counter: 100