Tuesday 9 August 2011

Python Lambda Function 2.

When I first started learning Python, one of the most confusing concepts to get my head around was the lambda statement. I’m sure other new programmers get confused by it as well and some of you are probably wondering what I’m talking about. So, in the spirit of education, let’s have a pop quiz:
Q. What is a lambda?
A. the 11th letter of the Greek alphabet
B. the craniometric point at the junction of the sagittal and lamboid sutures of the skull
C. the driver in an Arm Slave mecha that allows it to change the user’s thoughts into reality
D. the name of a series of Japanese rocket
E. anonymous (unbound) functions
If you guessed, F – all of the above, you got it right! Of course, in the context of this article, “E” is really the right answer. The Python lambda statement is an anonymous or unbound function and a pretty limited function at that. Let’s take a look at a few typical examples and see if we can find a use case for it.
The typical examples that one normally sees for teaching the lambda is some kind of boring doubling function. Just to be contrary, our simple example will show how to find the square root. First we’ll show a normal function and then the lambda equivalent:
import math
 
#----------------------------------------------------------------------
def sqroot(x):
    """
    Finds the square root of the number passed in
    """
    return math.sqrt(x)
 
square_rt = lambda x: math.sqrt(x)
If you try each of these functions, you’ll end up with a float. Here are a couple examples:

>>> sqroot(49)
7.0
>>> square_rt(64)
8.0
Pretty slick, right? But where would we actually use a lambda in real life? Maybe a calculator program? Well, that would work, but it’s a pretty limited application for a builtin of Python! One of the major pieces of Python that lambda examples are applied to regularly are Tkinter callbacks. We’ll take a look at that, but we’ll also take that information and try it with wxPython to see if works there just as well.

Tkinter + lambda

We’ll start with Tkinter since it’s included with the standard Python package. Here’s a really simple script with three buttons, two of which are bound to their event handler using a lambda:
import Tkinter as tk
 
########################################################################
class App:
    """"""
 
    #----------------------------------------------------------------------
    def __init__(self, parent):
        """Constructor"""
        frame = tk.Frame(parent)
        frame.pack()
 
        btn22 = tk.Button(frame, text="22", command=lambda: self.printNum(22))
        btn22.pack(side=tk.LEFT)
        btn44 = tk.Button(frame, text="44", command=lambda: self.printNum(44))
        btn44.pack(side=tk.LEFT)
 
        quitBtn = tk.Button(frame, text="QUIT", fg="red", command=frame.quit)
        quitBtn.pack(side=tk.LEFT)
 
 
    #----------------------------------------------------------------------
    def printNum(self, num):
        """"""
        print "You pressed the %s button" % num
 
if __name__ == "__main__":
    root = tk.Tk()
    app = App(root)
    root.mainloop()
Notice the btn22 and btn44 variables. This is where the action is. We create a tk.Buttoninstance here and bind to our printNum method in one fell swoop. The lambda is assigned to the button’s command parameter. What this means is that we’re creating a one-off function for the command, much like in the quit button where we call the frame’s quit method. The difference here is that this particular lambda is a method that calls another method and passes the latter an integer. In the printNum method, we print to stdout which button was pressed by using the information that was passed to it from the lambda function. Did you follow all that? If so, we can continue…if not, re-read this paragraph as many times as necessary until the information sinks in or you go crazy, whichever comes first.

wxPython + lambda

Our wxPython example is pretty similar to the Tkinter one, only a little more verbose:
import wx
 
########################################################################
class DemoFrame(wx.Frame):
    """
    Frame that holds all other widgets
    """
 
    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, wx.ID_ANY,
                          "wx lambda tutorial",
                          size=(600,400)
                          )
        panel = wx.Panel(self)
 
        button8 = wx.Button(panel, label="8")
        button8.Bind(wx.EVT_BUTTON, lambda evt, name=button8.GetLabel(): self.onButton(evt, name))
        button10 = wx.Button(panel, label="10")
        button10.Bind(wx.EVT_BUTTON, lambda evt, name=button10.GetLabel(): self.onButton(evt, name))
 
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(button8, 0, wx.ALL, 5)
        sizer.Add(button10, 0, wx.ALL, 5)
        panel.SetSizer(sizer)
 
    #----------------------------------------------------------------------
    def onButton(self, event, buttonLabel):
        """"""
        print "You pressed the %s button!" % buttonLabel
 
# Run the program
if __name__ == "__main__":
    app = wx.PySimpleApp()
    frame = DemoFrame().Show()
    app.MainLoop()
In this case, we create a two-parameter anonymous function with the lambda statement. The first parameter is evt and the second is the button’s label. These are passed on to the onButtonevent handler, which is called when the user clicks on one of the two buttons. This example also prints the button’s label to stdout.

Wrapping Up

The lambda statement is used in all kinds of other projects as well. If you Google a Python project name and lambda, you can find lots of live code out there. For example, if you search for “django lambda”, you’ll find out that django has a modelformset factory that utilizes lambdas. The Elixir plugin for SqlAlchemy also uses lambdas. Keep your eyes open and you’ll be surprised how many times you’ll stumble across this handy little function maker.

No comments:

Post a Comment