lambda vs. functools.partial

In Python we have a way for creating anonymous functions at runtime: lambda. With lambda we can create a function that will not be bond to any name. That is, we don’t need to def a function, and lambda can be used instead.

In [1]: f = lambda x: x+1
In [2]: f(1)
Out[2]: 2
In [3]: f(3)
Out[3]: 4
view raw gistfile1.txt hosted with ❤ by GitHub

Lambda can also be used to call another function by fixing certain argument:

In [1]: def sum(x, y):
...: return x+y
...:
In [2]: f = lambda n: sum(n,1)
In [3]: f(1)
Out[3]: 2
In [4]: f(3)
Out[4]: 4
view raw gistfile1.txt hosted with ❤ by GitHub

However, lambda functions use late binding for the arguents they get. That is, when you create a lambda function passing a variable as an argument, it’s not immediately copied, a reference to the scope is kept and the value is resolved when the function is called. Thus, if the value of that argument changes within that scope the lambda function will be called with the changed value. Lets see it in action:

In [1]: def sum(x,y):
...: return x+y
...:
In [2]: increment = 1
In [3]: f = lambda n: sum(n,increment)
In [4]: f(1)
Out[4]: 2
In [5]: f(3)
Out[5]: 4
In [6]: increment = 5
In [7]: f(1)
Out[7]: 6
view raw gistfile1.txt hosted with ❤ by GitHub

Unexpected? It depends, but it was definitely unexpected when I run into it. The solution for this is to bind early, by copying the value at the time of the creation of the function. We can do this 2 ways:

Using a fixed parameter in the lambda function:

In [1]: def sum(x,y):
...: return x+y
...:
In [2]: increment = 1
In [3]: f = lambda n,j=increment: sum(n,j)
In [4]: f(1)
Out[4]: 2
In [5]: f(3)
Out[5]: 4
In [6]: increment = 5
In [7]: f(1)
Out[7]: 2
view raw gistfile1.txt hosted with ❤ by GitHub

Using functools.partial:

In [1]: from functools import partial
In [2]: def sum(x,y):
...: return x+y
...:
In [3]: increment = 1
In [4]: f = pa
In [4]: f = partial(sum, increment)
In [5]: f(1)
Out[5]: 2
In [6]: f(3)
Out[6]: 4
In [7]: increment = 5
In [8]: f(1)
Out[8]: 2
view raw gistfile1.txt hosted with ❤ by GitHub

I personally like the second approach, using partial, since its more explicit and I really see option one as a workaround to how lambda functions work.

 

Leave a Reply