Let's Talk About Lambda Functions!

Alex Martelli aleax at aleax.it
Sat Jul 27 04:02:35 EDT 2002


Carl Banks wrote:
        ...
>>> sort a bunch of strings starting with the second letter:
        ...
> First, this has a pitfall: if x is not sortable for some reason, then

...then x is not a string, therefore the specs ("sort a bunch of
strings starting with the second letter", as above quoted) do not
apply.

Actually, the sort-with-argument solution proposed by the original
poster didn't meet the OP's specs: it ONLY sorted on the second
letter, giving an unspecified ordering to strings whose second
letters are equal -- no "starting with".  I read the specs as
meaning "as if each string started with its second letter and
then continued with the actual string", but one could read them
in other ways, e.g., sorting x[1:] (the slice that starts with
the second letter).

> the "right" way can throw an exception due to lexical ordering of
> tuples.  I encountered this when trying to sort classes that didn't
> have a __cmp__ method.  It seems that a better way is to use id(x) as
> the second element and x as the third: (x[1], id(x), x)

That would go back to unspecified ordering for strings with equal
second letters -- hardly sensible behavior.  It's just as much (or
as little) effort, if you DO want the second letter to be the ONLY
(rather than STARTING) sort key, to get a STABLE sort:

aux = [ (x[i][1], i) for i in xrange(len(a)) ]
aux.sort()
a[:] = [ a[i] for __, i in aux ]

> Second, if you're going to do it right often, you might want to wrap
> it up in a function like this:
> 
>     def sort_the_right_way (f, a):
>         aux = [ (f(x), x) for x in a ]
>         aux.sort()
>         a[:] = [ x for __, x in aux ]

You can do that, but then you risk 'freezing' in your higher order
function a suboptimal way to proceed -- e.g., no way this function
can be told to produce stable sorts, while keeping DSU inline it's
trivial to ensure the sort is stable.

The temptation to overgeneralize often accompanies the decision to
freeze some idiom into a higher-order function.  Keeping the idiom
inline avoids temptations to overgeneralize.  Thus, while it does
fly in the face of accepted wisdom, in Python sometimes it's wiser
to NOT refactor too many idioms and design patterns into ambitious
frameworks.  It's an observation which Tim also makes in his
chapter intro in the Cookbook, by the way -- with Python it's often
simpler to code idiomatically than to learn and adopt a framework
ambitious enough to cover all needed cases.  I would add the comment
that developing such frameworks is such child's play in Python, and
FUN, that one does it anyway -- even though they may not seem much
use afterwards:-).


> In which case, the temptation to use lambda returns:
> 
>     sort_the_right_way (lambda x:x[1], a)

Somebody ambitious enough to encapsulate DSU should surely proceed
by encapsulating typical accessors to be used with it in closure
factories, e.g.

        sort_the_right_way(make_items_acessor((1,)), a)

thereby avoiding lambda again.  It's only when stuff gets wrapped
at once too much (by freezing some subset of DSU into a higher
order function) AND not enough (by not supplying other higher
order functions to go with it by building needed closures) that
the _temptation_ of lambda'ing around perks up.  Easily avoided
by noting that even in that case one more def does it, of course.

It takes one more line -- saving lines is probably the core
fetish of lambda-enthusiasts, after all.  I propose again the
hypothesis that such obsession may be linked to traumas in
one's childhood...


Alex




More information about the Python-list mailing list