It's actually not just for lambdas; any function that takes default parameters will use the same syntax. For example
def my_range(start, end, increment=1):
ans = []
while start < end:
ans.append(start)
start += increment
return ans
(This is not actually how range works, I just thought it would be a simple example to understand). In this case, you can call my_range(5,10)
and you will get [5,6,7,8,9]
. But you can also call my_range(5,10,increment=2)
, which will give you [5, 7, 9]
.
You can get some surprising results with default arguments. As this excellent post describes, the argument is bound at function definition, not at function invocation as you might expect. That causes some strange behavior, but it actually helps us here. Consider the incorrect code provided in your link:
def create_multipliers():
return [lambda x : i * x for i in range(5)]
for multiplier in create_multipliers():
print multiplier(2)
When you call multiplier(2)
, what is it actually doing? It's taking your input parameter, 2, and returning i * 2
. But what is i
? The function doesn't have any variable called i
in its own scope, so it checks the surrounding scope. In the surrounding scope, the value of i
is just whatever value you left it -- in this case 4. So every function gives you 8.
On the other hand, if you provide a default parameter, the function has a variable called i
in its own scope. What's the value of i
? Well, you didn't provide one, so it uses its default value, which was bound when the function was defined. And when the function was defined, i
had a different value for each of the functions in your list!
It is a bit confusing that they've used the same name for the parameter variable as they did for the iterating variable. I suspect you could get the same result with greater readability with
def create_multipliers():
return [(lambda x, y=i: y*x) for i in range(5)]