ProgrammingBackend Developer

Explain how closures work in Python, how they differ from regular functions, and what their practical applications are?

Pass interviews with Hintsage AI assistant

Answer.

Background

The term "closure" was borrowed from functional programming and appeared in Python from the very beginning. Closures allow a function to remember the environment in which it was created, even when called outside that environment. This concept provides flexibility and enables the implementation of many patterns, including function factories and lazy computations.

Problem

In Python, functions are first-class objects. Sometimes, it is required for the inner function to use variables from the enclosing function's scope even after it has completed. Regular lexical scoping does not guarantee this upon function return. If such a function refers to the environment variables of its creation — a closure occurs.

Solution

A closure arises if the inner function refers to variables defined in the outer one, and that outer function returned the inner one to the outside. This is often used to create function factories, encapsulate state without classes, and construct functions with parameters "on the spot".

Code Example:

def make_multiplier(factor): def multiplier(x): return x * factor return multiplier mul2 = make_multiplier(2) mul3 = make_multiplier(3) print(mul2(10)) # 20 print(mul3(10)) # 30

Key Features:

  • A closure stores the values of the environment variables, even if the outer function has already completed.
  • The state in the inner function is essentially private and cannot be modified directly from the outside.
  • To modify a non-immutable variable in the outer function from within the closure, the nonlocal keyword is used.

Tricky Questions.

Can a closure maintain mutable state between calls if the variable is modified within the inner function?

Yes, if the nonlocal keyword is used within the inner function. Without nonlocal, the assignment creates a new local variable, not modifying the outer one.

def counter(): count = 0 def inc(): nonlocal count count += 1 return count return inc c = counter() print(c()) # 1 print(c()) # 2

Can private variables be implemented in Python using closures instead of classes?

Yes, closures provide a simple implementation of "private" variables that are inaccessible from the outside unless getters/setters are provided in the inner function.

Are closures only applicable to functions? Can closures be organized with lambdas in Python?

Yes, a closure can also form with lambda expressions, as they are similar to def in terms of lexical variable binding.

def make_power(n): return lambda x: x ** n square = make_power(2) cube = make_power(3) print(square(4)) # 16 print(cube(2)) # 8

Common Mistakes and Anti-patterns

  • Expecting that a closure automatically changes the outer variable when modified inside without nonlocal.
  • Capturing mutable objects in a closure and mutating them, unaware of debugging inconveniences.
  • Using a loop to create functions in a closure without proper variable binding (the "all functions see the last value of the variable" trap).

Real-life Example

Negative Case

A function factory forming handlers in a loop uses the loop variable inside a closure:

handlers = [] for i in range(3): def handler(x): return x + i handlers.append(handler) print([h(10) for h in handlers]) # [12, 12, 12]

Pros:

  • Simple, little code.

Cons:

  • All handlers refer to the same variable i, its last value 2 — behavior is unexpected for most.

Positive Case

Used a default argument to "fix" the value:

handlers = [] for i in range(3): def handler(x, j=i): return x + j handlers.append(handler) print([h(10) for h in handlers]) # [10, 11, 12]

Pros:

  • Binding the necessary value.
  • Predictable behavior.

Cons:

  • Need to remember this nuance and fix the closure manually, complicating code maintainability.