Python Closure

created:

updated:

tags: python

Python Supports Closures

Closures are referred to as “functions that refer to variables from the scope in which they were defined.”

Example

def sort_priority(values, group):
    def helper(x):
        if x in group:
            return (0, x)
        return (1, x)
    values.sort(key=helper)

In this example, helper method has access to the variable group due to the closures.

Example with a Variable Assignment

However, in another example, we can observe closures do not work for variable assignment:

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

This time, found variable remains as False value and it’s because closure works differently for a variable assignment.

If the variable is already defined in the current scope, it will just take on the new value. If the variable doesn’t exist in the current scope, Python treats the assignment as a variable definition.

For this reason, found = True within helper method is a new variable assignment, instead of re-assignment of value for found in sort_priority2 method.

Remedy

Using nonlocal Statement

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

The author of the book cautions against using nonlocal for anything more than simple due to its side effects. Instead, he recommends using a helper class.

Using a Helper Class

Writing a helper class may be a bit longer than using nonlocal, but it can be much easier to read.

class Sorter:
    def __init__(self, group):
        self.group = group
        self.found = False

    def __call__(self, x):
        if x in self.group:
            self.found = True
            return (0, x)
        return (1, x)

sorter = Sorter(group)
numbers.sort(key=sorter)
assert sorter.found is True

Order of Python Interpreter Treversing Scope:

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

References

  • Effective Python: Know How Closures Interact with Variable Scope