Python Cheat Sheet: Lambdas
Do you think list comprehensions are a tricky topic? After reading this post, you may well change your mind about what counts as “complicated” in Python.
A lambda is a small function defined right where you need it, typically for light data-processing jobs. There are two main reasons to use them:
- Speed: a compiler can optimize them, first by eliminating an actual function call, and then by opening the door to further internal code re-arrangements.
- Brevity: you write less code.
Here is a lambda that squares a number:
g = lambda x: x ** 2 # e.g. g(3) yields 9
Lambdas are most often paired with the data-processing functions filter, map and reduce.
To keep only part of an input, filter combined with a lambda works nicely:
print filter(lambda x: x % 2 == 0, xrange(0, 11))
# [0, 2, 4, 6, 8, 10]
To apply a transformation to every element of the input, use map:
print map(lambda x: x * 2, xrange(0, 11))
# [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
To boil the whole input down to a single result, use reduce. Note that the lambda must accept two arguments: at first it receives the first two elements, and afterwards it receives the previous result plus the next element, until the input runs out.
print reduce(lambda x, y: x + y, xrange(0, 11))
# 55 # 1 + 2 + ... + 10
Now for the more serious material.
print reduce(lambda x, y: x + y, map(lambda x: x * 2, xrange(0, 11)))
# 110 # 1*2 + 2*2 + ... + 10*2
def intersection(A, B):
S = set(A)
return filter(lambda x: x in S, B)
An answer like this probably isn’t what an interviewer expects. If you’re in an interview, make sure you can explain what’s happening before you write it down (for example, how a set works and what the complexity is in big O notation).
If the order of the remaining unique items doesn’t matter, calling set(A) is enough. Otherwise, we need to get creative:
def dedup(A):
S = set()
return filter(lambda x: x not in S and None == S.add(x), A)
A set’s add method always returns None, which evaluates to boolean False. To make the logic work, a small hack is needed (S.add() == None).
Also note that this solution keeps the first occurrence of each element and rejects the later duplicates. Solutions that keep the last or the n-th occurrence exist, but they’re much easier to write when the order of elements in the result doesn’t matter:
def dedup_last_unordered(A):
S = set()
map(lambda x: ((x in S and None == S.remove(x)) or True) and None == S.add(x), A)
return S
As you can see, that version is pushing past what lambdas are meant for. Writing it the “normal” way is much clearer:
def dedup_last_unordered(A):
S = set()
for i in A:
if i in S:
S.remove(i)
S.add(i)
return S
A solution for the n-th duplicate can’t be written purely with lambdas, because it needs a counter that is assigned and incremented, and lambdas don’t allow that. A “normal” approach would use a dictionary that stores the counter as the value, but I think it’s best to stop here.
That’s all for today. Hope you enjoyed it!