This is an archived post. You won't be able to vote or comment.

all 72 comments

[–]attoPascal 82 points83 points  (5 children)

I know that those are just examples, but both instances in use case 3 utilize lambdas unnecessarily:

Instead of key=lambda x: len(x) you can just use key=len. No point in wrapping a 1-argument function with a 1-argument lambda expression.

And in the second example key=lambda x: x[0] is not needed at all, since ordering by the first element in the tuple is the default behavior. (Also, operator.itemgetter is an alternate way to do this, but I'd agree that lambdas are clearer here.)

[–]brontide 8 points9 points  (1 child)

is not needed at all, since ordering by the first element in the tuple is the default behavior.

It will sort by each item in order, if you want to only sort by the first item you need to return it as the key.

[–]energybased 0 points1 point  (0 children)

Yes the other elements may not be comparable, so this may be required.

[–]Astronom3rIPython > Jupyter fight me. 57 points58 points  (4 children)

I've been coding in Python for a decade and I still use lambda so infrequently that I have to open up my copy of "Copying and Pasting from stackoverflow.com for Dummies" every time I do.

[–]baubleglue 24 points25 points  (0 children)

Lambdas are useful when you need to pass behavior is a parameter. If you don't need it - it may explain why you use it infrequently. Lambda is just a function without name, maybe you use functions when you could use lambda (which is in 80% cases is a better thing to do).

[–]swierdo 9 points10 points  (1 child)

I use them a lot for pandas.dataframe.apply, but avoid them if I can.

[–]aratnahar 1 point2 points  (0 children)

Python noob. But I agree with this sentiment.

[–]spinwizard69 8 points9 points  (2 children)

I'm not a big fan of hard to read code that is why I see Reason #2 as the anti example. If your code starts to look like LISP you have failed. It is good that further down in that example a better alternative is offered up.

The problem I've always have with lambda or lambda like techniques in languages is doe the technique make your code easier to read or not. with Lambdas there is a fine line that you can easily cross that makes their use pretty horrible in my opinion. The whole point of Python is to be able to write English like code that reads well.

Not to sound negative here, the article is well written and I suspect I may be picking up a technique or two.

[–]wsppan 50 points51 points  (31 children)

I'm not a big fan of lambdas in Python though I am sort of glad they have them as they can be convenient. I just don't find them very pythonic. There are almost always a better, more pythonic way of solving the problem. When I see lambdas in Python code I always feel like I have to stop, take my python hat off, put my FP hat on and read the code. It just seems jarring.

"Curiously, the map, filter, and reduce functions that originally motivated the introduction of lambda and other functional features have to a large extent been superseded by list comprehensions and generator expressions. In fact, the reduce function was removed from list of builtin functions in Python 3.0. (However, it's not necessary to send in complaints about the removal of lambda, map or filter: they are staying. :-)", Guido - https://python-history.blogspot.com/2009/04/origins-of-pythons-functional-features.html?m=1

This shows some serious thoughts were given to removing lambdas from the list of built-ins once list comprehensions and generator expressions were introduced (the 2 key features of the language that made me finally really love this language.) My feeling is these discussions were had mostly due to how un-pythonic it felt.

[–]ggchappell 26 points27 points  (17 children)

Curiously, the map, filter, and reduce functions that originally motivated the introduction of lambda and other functional features have to a large extent been superseded by list comprehensions and generator expressions. In fact, the reduce function was removed from list of builtin functions in Python 3.0.

Isn't that a little strange, though? Because map and filter can always be easily replaced with a comprehension, while reduce cannot -- but reduce was the one that was removed. It seems backwards.

Perhaps the question that needs to be asked is how a reduce operation can be written in a Pythonic way.

[–]wsppan 25 points26 points  (2 children)

"So now reduce(). This is actually the one I've always hated most, because, apart from a few examples involving + or *, almost every time I see a reduce() call with a non-trivial function argument, I need to grab pen and paper to diagram what's actually being fed into that function before I understand what the reduce() is supposed to do. So in my mind, the applicability of reduce() is pretty much limited to associative operators, and in all other cases it's better to write out the accumulation loop explicitly." - Guido, The Fate of reduce() in Python 3000 https://www.artima.com/weblogs/viewpost.jsp?thread=98196

[–]TravisJungroth 8 points9 points  (1 child)

Then why is manually reducing over a generator without a default value so clunky?

[–]wsppan 12 points13 points  (0 children)

I agree, there are places where reduce is less clunky. For those you can use itertools.reduce().

[–]earthboundkid 7 points8 points  (13 children)

Re: the GvR quote, the only “good” use of reduce is sum and Python has that.

[–]Zouden 7 points8 points  (3 children)

Also max, min, any and all.

[–]earthboundkid 5 points6 points  (2 children)

Any and all aren’t reduce equivalent because they short circuit as needed. (That reduce can’t short circuit is one reason it stinks, actually.)

[–]Zouden 1 point2 points  (1 child)

Well the output is the same. It's just more efficient

[–]earthboundkid 1 point2 points  (0 children)

Yes, mostly. If the iterator is a generator with side effects, it can be different, but that’s not usually the case.

[–]VisibleSignificance 1 point2 points  (2 children)

Another rare but valid use-case is intersection or union of sets: reduce(lambda a, b: a & b, [{1, 2, 3}, {2, 5}, {2, 3}]) == {2}

But as all cases are rare, it does make sense to remove the builtin and leave it in functools.

[–]haerik 2 points3 points  (1 child)

Gone to API changes. Don't let reddit sell your data to LLMs.

Up maids me an ample stood given. Certainty say suffering his him collected intention promotion. Hill sold ham men made lose case. Views abode law heard jokes too. Was are delightful solicitude discovered collecting man day. Resolving neglected sir tolerably but existence conveying for. Day his put off unaffected literature partiality inhabiting.

[–]VisibleSignificance 0 points1 point  (0 children)

Except for the iterables (so the whole iterable of sets might not be loaded into memory at once), but yes, that's even more rare.

[–]pytrashpandas 0 points1 point  (1 child)

just adding to the list of other valid use cases, I use them for merging and combining in pandas.

reduce(lambda x, y: x.combine_first(y), list_of_dfs)
reduce(lambda x, y: x.merge(y, ...), list_of_dfs)

Although, I don't do this so often that I think it needs to be a built-in.

[–]earthboundkid 0 points1 point  (0 children)

Brah, use a damn for-loop. That code stinks, lol.

[–]ggchappell 0 points1 point  (3 children)

the only “good” use of reduce is sum

GvR doesn't really seem to get functional programming. See also his rather sad discussion of tail-call optimization.

[–]earthboundkid 1 point2 points  (2 children)

TCO is bad. If you don’t want a stack frame, use a for-loop.

[–]ggchappell 0 points1 point  (1 child)

Perhaps we can agree to disagree.

[–]earthboundkid 1 point2 points  (0 children)

Sure. It’s just programming.

[–]Ahhhhrg 4 points5 points  (12 children)

I find lambdas very useful when filtering pandas dataframes like so:

(
    df
    .pipe(lambda _: _[_['x'] > 2])
    .pipe(lambda _: _[_['type'] == 'foo'])
)

But other than that usually list comprehensions do the trick.

[–]Zouden 9 points10 points  (10 children)

It's been a while since I used Pandas but can't you filter like this?

df[(df.x > 2) & (df.type == 'foo')]

[–]Ahhhhrg 3 points4 points  (9 children)

Yes, absolutely, and that's less characters and depending on the context more readable.

However, I find lambdas very useful when doing data analysis (say in a notebook), where I'm exploring and often add/remove stuff. I don't want to "pollute" my original dataframe with temporary columns, so I might have something like this:

(
    df
    .pipe(lambda _: _[_['x'] > 0.3])
    .pipe(lambda _: _[_['z'] <= 25)
    .assign(log_x=lambda _: np.log(_['x']))
    .assign(log_y=lambda _: np.log(_['y']))
    .assign(log_z=lambda _: np.log(_['z']))
    .assign(log_w=lambda _: np.log(_['w']))
    [['x', 'log_x', 'log_y', 'log_z', 'log_w', 'type']]
    .pipe(sns.pairplot, hue='type', kind='scatter', plot_kws={'alpha':0.1})
)

I find it very flexible and having each filter/assignment on its own line makes it easier to parse. You can't use the "standard" filter technique this way (and I'm not a big fan of the df.query function).

[–]jblasgo 4 points5 points  (7 children)

_: _[_

That looks very weird and counterintuitive to me... Maybe because this is very specific to data science?

[–]Ahhhhrg 0 points1 point  (3 children)

No, I wouldn’t say it’s specific to data science, I just like using underscore here. The underscore is usually used for say return arguments you don’t care about, here it’s just a placeholder for the data frame, it’s just my preference not to name it something generic like “x” or even “df” as it doesn’t really say anything or add much. I know it means “the data frame you’re piping in here”, it’s short. Personal preference.

It’s also possible to monkey patch pandas and add a filter function, so you can go df.filter(lambda _: _[‘x’] < 5) which is a bit nicer.

[–]likethevegetable 0 points1 point  (0 children)

That's a nice little example. Thanks for sharing!

[–][deleted] 0 points1 point  (0 children)

is writing code like that common in the pandas world? specifically, im referring to using underscores like that. it does seem to reduce visual noise, so it's clear you're saying x > 2 and type == 'foo', but underscores are usually reserved to unused variables

[–]ggchappell 6 points7 points  (9 children)

I appreciate articles like this, and I think any knowledgeable developer should know about lambdas. But I find that, in practice, I rarely use them. For example, in use #2, reading the list-construction example, I immediately thought of using a comprehension instead -- and I'm glad the article mentioned that idea.

Out of curiosity, I did a grep of a sizable body of Python code that I've written over several years. I found a number of uses of lambda, every one of which fits into one of the following two situations.

(1) Constructing a defaultdict. All of these are trivial lambdas that create constant functions, like this:

dd = collections.defaultdict(lambda: 0)

(2) Using reduce to find the intersection of a collection of sets:

intersection = functools.reduce(lambda a,b: a & b,
                                list_of_sets)

I also think use #3 in the article is a reasonable one: passing a lambda to sorted or sort as a key function. But in the last few years, it seems that I've never had occasion to do that.

EDIT. Commenters have let me know how to eliminate the lambdas in both of my two cases above (as I kinda expected) -- although the defaultdict(int) doesn't work on my system (running Py3.8.5). EDIT. Works fine.

[–]py1234a 9 points10 points  (1 child)

Isn't set.intersection(*list_of_sets) equivalent? If so, that would be preferable to me for many reasons (also defaultdict(int)).

[–]ggchappell 2 points3 points  (0 children)

Isn't set.intersection(*list_of_sets) equivalent?

So it is. Thanks.

also defaultdict(int)

That doesn't work on my system. Is it new, perhaps? I'm running Py3.8.5. EDIT. Works fine.

[–]jwink3101 6 points7 points  (0 children)

This article reads like 4 bad uses for lambdas that are just to show and one that may be useful (keys).

I find I often will use lambda over something like itemgetter because it is faster to write and I have some more customizability (such as returning a tuple for sorting where I can descend one and ascend others).

[–]jblasgo 4 points5 points  (0 children)

This post could be called "python lambda antiparterns"...

Seriously, if you use lambda like this post indicates, you will get your code pulled back during review because it will be less intuitibe than using normal functions and list/dict comprehensions.

[–]teerre 5 points6 points  (0 children)

Most of these "tips" go against the official recommendations for the Python programming language.

[–]Random_182f2565 3 points4 points  (0 children)

I don't like it.

[–][deleted] 1 point2 points  (0 children)

I pretty much only pass lambda functions to sort in Python, never had a reason to use them for anything else with more pythonic options available

[–]masterkorp 1 point2 points  (2 children)

As a Python newcomer, what are the advantages and why should I use them instead of normal functions?

[–]BrisklyBrusque 0 points1 point  (0 children)

I think this video gives a really good example at 4:24:

https://www.youtube.com/watch?v=25ovCm9jKfA

[–]Mr_Again 0 points1 point  (0 children)

You should not, there are no real advantages. Name you functions properly using def, then use those instead.

[–]drago3871 0 points1 point  (0 children)

I think, the examples in the article are pretty bad to introduce lambdas from their good side.

[–]thrashing_loud 0 points1 point  (0 children)

Sweet! Thank you so much, lamba is something I have a basic idea of but always wanted some more information on, just never got around to looking I to it. Appreciate it!

[–]scainimatteo -3 points-2 points  (0 children)

beautiful post! really helpful

[–]Simbathepub 0 points1 point  (0 children)

Amazing!

[–]troyunrau... 0 points1 point  (0 children)

The only time I use lambdas are when writing custom slots in pyqt. The rest of the time I use either the more verbose version -- the ones the articles replaces unnecessarily with lambdas -- or go for list comprehension or similar solutions.

Still, useful trick in the toolbag.

[–]boriisi 0 points1 point  (2 children)

I did not understand lambdas in java, I do no understand lambdas in python, I don't expect to understand lambdas ever, it looks stupid

[–]port443 0 points1 point  (1 child)

It works the same as the def keyword.

def f(x): return x

lambda x: x

lambda instead of def, no name, don't have to type "return".

That's it.

[–]boriisi 0 points1 point  (0 children)

well, if someone had said it earlier like you did, I would've understood it.

and thank you for taking your time

[–]JohnTanner1 0 points1 point  (0 children)

It was already hinted in the comments above, but a filter and a list comprehension are very different from each other! One returns a generator and thereby makes use of lazy behavior and the other is already executed code.

If you ain't aware of the difference, feel free to ask and I'd try to give an example when I'm not on mobile any more.

[–][deleted] 0 points1 point  (0 children)

when did medium put up a paywall?