LoginSignup
1
1

More than 5 years have passed since last update.

EP 15 Know How Closures Interact with Variable Scope

Posted at
  • Closure functions can refer to variables from any of the scopes in wihch they were defined.
  • By default, closures can't affect enclosing scopes by assigning variables.
  • In Python 3, use the nonlocal statement to indicate when a closure can modify a variable in its enclosing scopes.
  • In Python 2, use a mutable value (like a single-item list) to work around the lack of the nonlocal statement
  • Avoid using nonlocal statements for anything beyound simple functions.

Effective Python

Scope

Say you want to sort a list of numbers but prioritize one group of numbers to come first.

def sort_priority(values, group):
    def helper(x):
        if x in group:
            return (0, x)
        return (1, x)
    values.sort(key=helper)
In [2]: numbers = [8, 3, 1, 2, 4, 5, 7,6]

In [3]: group = {2, 3, 5, 7}

In [4]: sort_priority(numbers, group)

In [5]: print(numbers)
[2, 3, 5, 7, 1, 4, 6, 8]
  • Python supports closures: functions that refer to variables from the scope in which they were defined. This why the hlper function is able to access the group argument to sort_priority.
  • Functions are first-class objects in Python, meaning you can refer to them directly, assign them to variables, pass them as arguments to other functions, compare them in expressions and if statements, etc. This is how the sort method cna accept a closure function as the key argument.
  • Python has specific rules for comparing tuples. If first compares items in index zero, then index one, then index two, and so on. This is why the return value from the helper closure causes the sort order to have two distinct groups.

Cause Scope Bug

def sort_priority2(numbers, group):
    found = False # Scope: sort_priority2
    def helper(x):
        if x in group:
            found = True # Scope: helper -- Bad
            return 0, x
        return 1, x
    numbers.sort(key=helper)
    return found
>>> l = [4, 6,2,5,7,9, 0]
>>> found = sort_priority2(l, [2,5,8])
>>> found
False
>>> l
[2, 5, 0, 4, 6, 7, 9]

  1. The current function's scope
  2. Any enclosing scope (like other containing functions)
  3. The scope of the module that contains the code(also called the global scope)
  4. The built-in scope(that contains functions like len and str)

Getting Data out

nonlocal Python 3

def sort_priority2(numbers, group):
    found = False # Scope: sort_priority2
    def helper(x):
        nonlocal found
        if x in group:
            found = True # Scope: helper -- Bad
            return 0, x
        return 1, x
    numbers.sort(key=helper)
    return found

The nonlocal statement is used to indicate that scope traversal should happen upon assiginment for a specific variable name. The only limit is that nonlocal won't traverse up to the module-level scope (to avoid polluting globals)

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1