Iota, Kappa… Lambda!

Those new to Python can stumble on the humble lambda. It does seem odd, with it’s odd little syntax and it’s wee beady colons. What does it mean? Why does it look so odd? Let’s shed some light on it, shall we?

The lambda comes from Lambda Calculus, and is an integral part of two languages I claim to love, LISP and Scheme. Python took all its best stuff from these languages, and so it is only natural that lambda would be pilfered as well.

Lambda is a function that is not bound to a name. Most functions in Python follow this form:


def myFunction():
    pass

When you enter this code into the Python interpreter, it sets aside a string ‘myFunction’ and binds it to a piece of compiled code. Python is, in essence, a giant dictionary made up of other dictionaries, that map strings to pieces of code, or values, etc. Lambda is used when you want to manipulate small functions that don’t need cumbersome names and docstrings – small, temporary functions.

Much of the work lambda was doing has been replaced by list comprehensions. Before those, Python users relied on functions like filter(). For example, to get a list of even numbers from a list, one would could do the following:


>>> filter(lambda x: x % 2 == 0, xrange(9))
[0, 2, 4, 6, 8]

Much more convenient than writing out an isEven function, especially if you are only using it in one or two places. You can see how the list comprehension is similar, and, one could argue, cleaner:


>>> [x for x in xrange(9) if x % 2 == 0]
[0, 2, 4, 6, 8]

If you’ve messed around with lambda, you are probably aware it is not like a normal function. Lambda is an expression, which means you can’t declare variables, do complicated control statements or use a return statement. With lambda, the result of the function is immediately returned. Consider a simple example:


>>> addFive = lambda x: x + 5
>>> addFive(7)
12

Notice, the result of x + 5 is immediately returned – no assignment, no return statement. Lambda is often used to bind arguments to functions dynamically, where the result of the function is returned immediately.

I often use lambda as a predicate for a function that acts like filter(). Consider a function that find all files for which a predicate returns true:


import os

def find(root, pred=lambda x: True):
    root = os.path.abspath(root)
    result = []
    def _tm(dr, pred, output):
        for i in os.listdir(dr):            
            pl = os.path.join(dr, i)
            if pred(pl):
                output.append(pl)
            if os.path.isdir(pl):
                _tm(pl, pred, output)
    _tm(root, pred, result)
    return result


if __name__ == '__main__':
    for t in find('c:/Users/user/Documents/maya', pred=lambda x: x.endswith('.mel')):
        print(t)

The lambda is a default argument that simplifies the coding – you can always just call the predicate function in the code, so less branching and possible code errors.

It can be used for binding partial arguments, but functools.partial handles that and is a bit more conventional Python.

Lambda is useful in conjunction with Python’s various sort methods where it can serve as a key or cmp function – this is handy when, say, sorting the items in a dict by value:


dct = {'test_a' : 3,
       'test_b' : 1,
       'test_c' : 2}

>>> for k in sorted(dct.items(), key=lambda x: x[1]):
...     print(k)

('test_b', 1)
('test_c', 2)
('test_a', 3)

Lambda and Class Properties

Python properties are an area where lambda can play a role. Consider properties inherited by derived classes:


class A(object):
    @property
    def data(self):
        return 'A'

class B(A):
    def data(self):
        return 'B'

The flaw in this becomes evident when the property is accessed.


>>> a = A()
>>> print('a: %s' % a.data)
a: A

>>> b = B()
>>> print('b: %s' % b.data)
b: <bound method B.data of <__main__.B object at 0x00000000023B15C0>>

B’s data method needs the @property decorator to work, but that seems to be non-intuitive with regard to how inheritance works. The property still doesn’t give me what I’m looking for:


class A(object):
    def getData(self):
        return 'A'

    data = property(getData)

class B(A):
    def getData(self):
        return 'B'


>>> b = B()
>>> print('b: %s' % b.data)
A

In this situation, lambda comes through:


class A(object):
    def getData(self):
        return 'A'

    data = property(lambda self: self.getData())

class B(A):
    def getData(self):
        return 'B'

>>> b = B()
>>> print('b: %s' % b.data)
B

This does the trick – because the property is bound to the class, the first method passed to the class is self, and self is an instance of the derived class, so the derived getData method will be called. The property can be updated to call a derived setter, as well, if there was a setData method.

The Moral of the Story
Lambda is useful for creating simple functions that evaluate expressions that can be used to perform simple calculations or compose other function calls on the fly. While many of its most common uses have been supplanted by list comprehensions, it is still a handy tool to have in the toolbox.

Advertisements
Iota, Kappa… Lambda!