Article Categories
- All Categories
-
Data Structure
-
Networking
-
RDBMS
-
Operating System
-
Java
-
MS Excel
-
iOS
-
HTML
-
CSS
-
Android
-
Python
-
C Programming
-
C++
-
C#
-
MongoDB
-
MySQL
-
Javascript
-
PHP
-
Economics & Finance
Why do Python lambdas defined in a loop with different values all return the same result?
Before understanding why Python lambdas defined in a loop with different values all return the same result, let us first learn about Lambda expressions and the concept of late binding closures.
Python Lambda
Lambda expressions allow defining anonymous functions. A lambda function is an anonymous function i.e. a function without a name. Let us see the syntax ?
lambda arguments: expressions
The keyword lambda defines a lambda function. A lambda expression contains one or more arguments, but it can have only one expression.
Example
Let us see an example ?
myStr = "Thisisit!" (lambda myStr : print(myStr))(myStr)
Thisisit!
The Problem: Late Binding Closures
Let's say we are finding squares using lambdas. When we define different lambdas in a loop, they all reference the same variable from the outer scope ?
squares = []
for a in range(5):
squares.append(lambda: a**2)
print(squares[2]()) # Expected: 4, Actual: 16
print(squares[0]()) # Expected: 0, Actual: 16
print(squares[4]()) # Expected: 16, Actual: 16
16 16 16
Why This Happens
The variable a is not local to the lambdas, but is defined in the outer scope. The lambda functions access a when they are called, not when they are defined. At the end of the loop, a equals 4, so all functions return 42 = 16.
We can verify this by changing the value of a after the loop ?
squares = []
for a in range(5):
squares.append(lambda: a**2)
a = 8 # Change the value
print(squares[2]()) # Now returns 8**2 = 64
64
Solution: Using Default Arguments
To fix this, we capture the value of a as a default argument, creating a local variable for each lambda ?
squares = []
for a in range(5):
squares.append(lambda n=a: n**2)
print(squares[0]()) # Returns 0
print(squares[2]()) # Returns 4
print(squares[4]()) # Returns 16
0 4 16
Alternative Solutions
Using functools.partial
from functools import partial
def square(x):
return x**2
squares = []
for a in range(5):
squares.append(partial(square, a))
print(squares[2]()) # Returns 4
4
Using List Comprehension
squares = [lambda n=a: n**2 for a in range(5)] print(squares[1]()) # Returns 1 print(squares[3]()) # Returns 9
1 9
Comparison
| Method | Captures Value? | Readability | Best For |
|---|---|---|---|
| Default argument | Yes | Good | Simple cases |
| functools.partial | Yes | Excellent | Existing functions |
| List comprehension | Yes | Excellent | Creating collections |
Conclusion
Python lambdas in loops suffer from late binding closures they capture variables by reference, not by value. Use default arguments lambda n=a: n**2 or functools.partial to capture the current value during each iteration.
