
Enclosing Scope and Free Labels in Python
In Python's ecosystem, when you reference a variable outside its defined scope, it earns the moniker of a "free" or "unbound" label.
This concept boils down to Python's flexibility with variable scope. If a variable isn't defined within a function, Python embarks on a search through the surrounding scopes to locate a definition.
In essence, if a function employs a free variable without defining it, this variable will naturally align with the nearest enclosing scope—the space outside the function where it finds a definition.
Python's approach to scoping is methodical, retracing steps up the scope hierarchy to secure a variable definition or, failing that, signaling an error for the undefined variable.
Interestingly, free labels are statically linked to their enclosing scope at the function's definition point, not during its execution. This ensures Python's reference resolution is based on the code's initial interpretation rather than its runtime behavior.
Let's delve into a practical example.
Consider the following script:
var = 'I am defined outside the function'
def foo():
print(var)
foo()
Upon invoking the `foo()` function, it aims to print the `var` variable's value. Failing to locate `var` within its local scope, Python broadens its search and successfully finds it in an outer scope.
Thus, it yields the following output:
I am defined outside the function
This example illustrates that even without an internal definition of `var` within `foo`, the function accesses it through the encompassing outer scope.
Contrastingly, identifying a label's definition locally leads Python to establish a local variable instance.
var = 'I am defined outside the function'
def foo():
var = 'I am defined inside the function'
print(var)
foo()
Here, Python outputs the value assigned to the local variable.
I am defined inside the function
To tweak the scope of a local variable, Python introduces the nonlocal statement.
Employing the nonlocal Statement
The nonlocal statement is your toolkit for scope adjustment of a local variable, enabling reference to a nested, non-global variable located in an enclosing scope.
nonlocal
In practical terms, `nonlocal` grants the capability to modify a variable situated in any enclosing scope outside of the global arena.
Here’s how it plays out:
def outer_function():
var = "Local in outer_function"
def inner_function():
# Signaling the intent to utilize the upper scope's var
nonlocal var
var = "Modified by inner_function"
inner_function()
print(var)
outer_function()
Without `nonlocal`, assigning a new value to `var` within `inner_function` would prompt Python to instantiate a new local variable, isolating it from the outer function's `var`.
The nonlocal declaration, however, modifies `var`'s scope to align with `outer_function`, thus affecting the variable across nested scopes.
The outcome is as follows:
Modified by inner_function
In this scenario, `outer_function` acts as the enclosing function, with its scope aptly termed enclosing.
This mechanism allows for nuanced control over variable scope, seamlessly transforming a local variable's domain into an enclosing one.
Bear in mind, the concept of enclosing scopes is exclusive to the domain of functions.