I come from a functional programming background, so I a lot of love for functions and so-called anonymous functions or lambdas. However, I have realised that I don't make use of Python's lambda syntax much, and I wanted to articulate why. This may mostly sound pretty negative towards lambdas, but bear in mind I'm certainly not against lambdas in general.
A lambda
expression could always be avoided by simply defining the function in question. So the question becomes when is a lambda
more readable than a definition? That is almost always because the function you're attempting to create is so simple that the definition syntax gets in the way. It is often used when the function in question is going to be used as an argument to some method. For example the sort
method on a list takes as argument they key
to be used when comparing the elements. So if you have a list of items and you want to sort them by their prices you can do something such as:
list_of_items.sort(key=lambdax:x.price)
We could of course have gotten the use of lambda
with a definition:
defprice(item):returnitem.pricelist_of_items.sort(key=price)
This is not quite equivalent because it introduces a new name into the current scope that would not otherwise have been there, but I don't ever recall reaching for a lambda
expression in order to avoid polluting the name space.
We could even define a generic higher-order function to make a function out of attribute access.
deff_attr(attr_name):defattr_getter(item):returngetattr(item,attr_name)returnattr_getterlist_of_items.sort(key=f_attr('price'))
This is surely worse than the first two attempts, but if you have multiple sorts to do, then using definitions can get tedious:
defprice(item):returnitem.pricelist_of_items.sort(key=price)do_something(list_of_items)defquantity(item):returnitem.quantitylist_of_items.sort(key=quantity)do_something(list_of_items)defmargin(item):returnitem.marginlist_of_items.sort(key=margin)do_something(list_of_items)
as compared with the lambda version:
list_of_items.sort(key=lambdax:x.price)do_something(list_of_items)list_of_items.sort(key=lambdax:x.quantity)do_something(list_of_items)list_of_items.sort(key=lambdax:x.margin)do_something(list_of_items)
The f_attr
version is similar:
list_of_items.sort(key=f_attr('price'))do_something(list_of_items)list_of_items.sort(key=f_attr('quantity'))do_something(list_of_items)list_of_items.sort(key=f_attr('margin'))do_something(list_of_items)
In fact that could be done with a loop:
forattributein['price','quantity','margin']:list_of_items.sort(key=f_attr(attribute))do_something(list_of_items)
I think the lambda
version is at least sometimes more readable. However, I often miss the self-documenting nature of a definition, in that giving the function a name acts as some pretty decent documentation especially for the kind of simple function you might otherwise use a lambda
expression for.
So, for a lambda
expression to be the right choice, the function in question has to be so simple as to not benefit from the extra documentation afforded by its name (such as simple attribute access).
I just don't seem to find myself in that situation very often. Perhaps I'm just not making my own functions general enough by accepting functions as arguments.