When function statements are executed they are bound to their (lexically) enclosing scope.
In your snippet, the lambdas are bound to the global scope, because for
suites are not executed as an independently scoped unit in Python. At the end of the for
loop, the num
is bound in the enclosing scope. Demo:
for num in range(1, 6):
pass
assert num == 5 # num is now bound in the enclosing scope
So when you bind identifiers in the for
loop you're actually manipulating the enclosing scope.
for num in range(1, 6):
spam = 12
assert num == 5 # num is now bound in the enclosing scope
assert spam == 12 # spam is also bound in the enclosing scope
Same deal for list comprehensions:
[num for num in range(1, 6)]
assert num == 5
Mind blowing, I know. Anywho, with our newfound knowledge, we can determine that the lambdas you are creating are referring to the (single) num
identifier bound in the enclosing scope. That should make this make more sense:
functions = []
for number in range(1, 6):
def fun():
return number
functions.append(fun)
assert all(fun() == 5 for fun in functions)
assert all(fun() is number for fun in functions)
And here's the coolest part that demonstrates it even more:
# Same as above -- commented out for emphasis.
#functions = []
#for number in range(1, 6):
# def fun():
# return number
# functions.append(fun)
#assert all(fun() == 5 for fun in functions)
#assert all(fun() is number for fun in functions)
number = 6 # Rebind 6 in the scope and see how it affects the results.
assert all(fun() == 6 for fun in functions)
So the solution to this, of course, is to make a new enclosing scope for each number
you want to bind. In Python, you can create new enclosing scopes with modules, classes, and functions. It's common to use a function just to create new enclosing scope for another function.
In Python, a closure is a function that returns another function. Kind of like a function constructor. Check out get_fun
in the following example:
def get_fun(value):
""":return: A function that returns :param:`value`."""
def fun(): # Bound to get_fun's scope
return value
return fun
functions = []
for number in range(1, 6):
functions.append(get_fun(number))
assert [fun() for fun in functions] == range(1, 6)
Since get_fun
is a function, it gets to have its own internal scope. Every time you call get_fun
with a value, a little table is created to keep track of bindings within it; i.e. it says, "Within this scope, the value
identifier points to the thing that was passed." That scope goes away at the end of the function execution, unless there's a reason for it to hang around.
If you're returning a function from within a scope, that's a good reason for parts of the "scope table" to hang around -- that function you're returning could reference things from that scope table when you call it later on. For that reason, when fun
is created within get_fun
Python tells fun
about get_fun
's scope table, which fun
keeps handy for when it's needed.
You can read more about the details and technical terminology (which I softened a bit) in the Python docs on the execution model. You can also look at the parts of the enclosing scope that a function refers to with print fun.__closure__
. In the above, we see the reference to the value
, which happens to be an int:
# Same as before, commented out for emphasis.
#functions = []
#for number in range(1, 6):
# functions.append(get_fun(number))
#assert [fun() for fun in functions] == range(1, 6)
print functions[0].__closure__
# Produces: (<cell at 0x8dc30: int object at 0x1004188>,)