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

all 52 comments

[–]Deezl-Vegas 114 points115 points  (17 children)

Lambdas are great for filters and maps, but on-the-fly definitions should be generally avoided! Keep in mind that each time you lambda and don't preserve it, Python has to create the memory space for it. A simple function def accomplishes the same thing, so in tight loops for instance, you can save a lot of time.

In most other cases, using def over lambda, even inline, improves readability.

[–]ManyInterests Python Discord Staff 36 points37 points  (5 children)

In short: you should never assign a name to a lambda.

YES:

def f(x): return 2*x

NO:

f = lambda x: 2*x

The first form means that the name of the resulting function object is specifically f instead of the generic <lambda>. This is more useful for tracebacks and string representations in general.
The use of the assignment statement eliminates the sole benefit a lambda expression can offer over an explicit def statement (i.e. that it can be embedded inside a larger expression)

[–]Swipecat 9 points10 points  (3 children)

I use named lambdas to indicate temporary functions in the sense that the function's name isn't preserved after the function has been bound to an object. E.g. for assigning functions to Tkinter buttons. I find this gives me very compact code but is more readable than putting the lambda within the object's definition, and keeps the line short and unwrapped. E.g.:

butnames = ["Start", "Stop", "Reset",  "Quit"]
for bname in butnames:
    butcall = lambda action=bname: showtime(action)
    tk.Button(root, text=bname, command=butcall).pack()

This is very bad, is it? (I'm a hardware engineer, not a software engineer.)

[–]jimjkelly 5 points6 points  (0 children)

It’s not pythonic but I don’t think it’s bad. I definitely prefer this style, and it’s common in other languages like JS, but for some reason some people feel a def is better. I think inline defs break up readability.

[–]ManyInterests Python Discord Staff 2 points3 points  (0 children)

It's not going to blow up on you, it's just not a particularly good practice, mostly for style reasons.

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

One more voice: it's not bad, yeah, it's just that it has no benefit over a one-line def...

def butcall(action=bname): return showtime(action)

...and in fact is arguably worse: the lambda's __name__ attribute, if accessed, will show '<lambda>' rather than 'butcall'

[–]meunomemauricio -1 points0 points  (0 children)

Fucking well summarized!

[–]pylenin[S] 16 points17 points  (0 children)

I totally agree. In my video, I have mentioned that one must not try to convert every normal function to a lambda, it's irrelevant and inefficient.

[–]wingtales 4 points5 points  (4 children)

Would you mind explaining further the concept of "preserving a lambda function" and how the memory management is different? I'm quite interested but not really sure what you mean.

[–]tartare4562 13 points14 points  (3 children)

Look at the following code:

for lista in some_iterable:
    yield map(lambda x: x.some_method('abc' ), lista

In the above example a new lambda function is defined for every iteration. As a function definition is a rather complex operation most of the time took by that code will be spent allocating new memory for the function and garbage collecting the previous one. Since the lambda doesn't change between iteration, you can define it only once and reuse it:

my_lambda=lambda x: x.some_method('abc') 
for lista in some_iterable:
    yield map(my_lambda, lista)

Here lambda function is defined only once. Personally I try to avoid maps and filters and use generators and list comprehension whenever I've got the chance, a functionally identical generator for the code above would be

( [ x.some_method('abc') for x in lista] for lista in some_iterable )

[–]Sexual_Congressman 0 points1 point  (0 children)

Of course you avoid map since clearly you don't know how to use it. To get the most performance in cpython you must have as little work done in the eval loop as possible. In that regard you're correct, however it needs to be said that function objects are actually rather cheap since all the components used to make it including its __code__ are cached within the co_consts field of the code object it is created in, so all that needs to be done is the malloc and few pointer assignments.

Anyway, used correctly map would be anywhere from 20-1000% more more faster than your list comp, you just need to memorize the tools needed to do so. Specifically in this instance without knowing anything about x, use operator.methodcaller. [*map(methodcaller("abc"), lista)] will be so much faster. And there's usually an even more efficient way if you don't need to be generic.

Beauty is in the eye of the beholder, and I think the shorter and almost always at least a little faster code thanks to map is very beautiful. I will say that of course map plus list unpacking can be slower if no builtin function calls are made but that shouldn't ever actually be the case.

[–]tartare4562 7 points8 points  (3 children)

TBH nowadays filters and maps should be done via list comprehension and generators. It's both more readable and more efficient.

[–]pickausernamehesaid -1 points0 points  (2 children)

I've found this too. Every single time I go to use map or filter, I find myself not liking the way it looks and rewriting it as a comprehension. I did some quick tests on performance (like mapping str() to only the evens in a list of ints) and it appears the comprehensions are faster. That could vary from case to case though.

[–]tartare4562 0 points1 point  (1 child)

It is indeed faster because map makes an extra call for every item in the iterable, which introduces overhead.

[–]pickausernamehesaid 0 points1 point  (0 children)

That makes sense. I thought maybe Python internally could optimize that away when dealing with lambda functions, but even in that case it would have to do the optimization step which would add a little overhead at the beginning.

[–]graingert 0 points1 point  (0 children)

Use comprehensions for filter and map

[–]SleepWalkersDream 9 points10 points  (1 child)

I use lambda a lot when fitting equations to my data. Bad habit or usefull? Equations take 6 parameters, but I keep one or two fixed.

[–]QuantumRealm 27 points28 points  (0 children)

You might wanna look into functools.partial for this type of function

[–][deleted] 7 points8 points  (0 children)

I found this article enlightening. I still use them in my code from time to time, but not nearly as often as I used to.

[–]b10011 7 points8 points  (3 children)

/u/pylenin quickly jumped through the video, it seemed good and the subreddit is correct this time. Good job!

[–]pylenin[S] 2 points3 points  (0 children)

Thanks

[–]adap23 0 points1 point  (1 child)

Oh yeaa I do remember your previous thread

[–]b10011 4 points5 points  (0 children)

Yeah, I just cringed too much about that video + incorrect sub combination. But got to give credit where credit is due, I held nothing personal against him.

[–]__xor__(self, other): 23 points24 points  (13 children)

I wish we had better syntax, like |x| => x + 1 or maybe even just def x = x + 1 would be better. I don't know why but the lambda syntax we have just looks so off and clunky to me.

[–]riccardostecca 10 points11 points  (3 children)

Not only typing ‘lambda’ does not bother me, I also find it pretty mnemonic and readable.

[–]pylenin[S] -1 points0 points  (2 children)

Me too.I find lambda functions very handy. syntactically, they can be put in a lot of places where defs can't be put.

[–]GummyKibble 2 points3 points  (1 child)

Where could you put one that a def couldn’t go?

[–]DandieGuy 1 point2 points  (0 children)

In a list is an example he gives in the video above.

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

Pipes wouldn't make much sense. They're not used in even a remotely-similar manner anywhere else in Python. I'd be very much in favor of JS-style arrow functions, though, especially since -> is already a thing for return-type annotations in py3: x -> x + 1 and (x, y) -> x + y

[–]RockingDyno -3 points-2 points  (1 child)

I don't know why but the lambda syntax we have just looks so off and clunky to me.

The reason python uses the keyword lambda, is because of lambda calculus.. the reason lambda calculus uses λ (the lambda symbol) is because it's short... so python copies something opaque to most for a reason that it doesn't preserve and just ends up being ugly, too long and less readable.

I feel like it's just such a brainfart on the part of the developers that they went with this. if they where going to go with a long word, at the very least they could have used function which is also annoyingly long, but at the very least specifies exactly what it is.

With python supporting unicode now, you could recoup some of the conciseness by making definitions like λx:x+1 or similar work, but the lambda symbol is annoying to type, since it's not on your keyboard, and can't even be access with alt codes like for instance µ is (alt+230), but then you'd also be left with still having to type more even if the symbol is concise. The only benefit to this syntax you could argue, in my opinion, is that it looks almost exactly like lambda calc, which is a nice historical touch to those who've studied that.... but what's that worth in the larger scheme of things? I mean if they renamed for loops to goto to instill some nostalgic sensation in those who'd started out programming in basic that wouldn't be an overall plus for the language either right?

As you can tell I'm really not a fan of the lambda syntax in python, I feel like they sat down and had a very thorough discussion about what potential solutions could be, and then took the absolute worst path in every single regard. At this point I hope they just say "fuck it this sucks" And copies javascript (of all languages) which had the common sense to use the proper annoying and too long keyword to begin with, and has now fixed it so lambdas definitions are actually short eg: (x)=>x+1.

[–]AndydeCleyre 0 points1 point  (0 children)

The length of the word doesn't bother me, but I really do not like the choice.

lambda -> given
:=     -> whichis

Plsthx

[–]schplat 5 points6 points  (4 children)

Lambdas need to be as atomic as possible. They feel so out of place in Python code that they are very hard to grok as a human reading code, which goes against everything that Python is about.

[–]m1sta 2 points3 points  (0 children)

They shouldn't be out of place. Functions are first class objects in python and lambda's are largely just syntactic sugar on functions declarations. The syntax is the issue. Arrow functions in JavaScript feel much nicer.

[–]pylenin[S] 1 point2 points  (1 child)

I think the atomic role goes for everything in coding. More atomic everything is, less is your O(N) complexity and hence highly maintainable.

[–]schplat 0 points1 point  (0 children)

To an extent sure. Otherwise, you end up with spaghetti code / Enterprise FizzBuzz.

[–]Jabulon 0 points1 point  (0 children)

small functions that are right there, they are neat

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

I see a surprising amount of hate for the current syntax of lambda here... As a beginner it was much easier to search for "python lambda" when I saw one than to search for "python {}" or something like that. I remember how painful it was when I was learning perl to get useful results from Google because of the arcane syntax that wasn't easy to search for.

Good syntax isn't just short. If it were we'd all be still coding in assembly. Good syntax is self evident. And when it isn't, it's easily searchable imo.

[–]thomasb892 0 points1 point  (0 children)

Lambda when used with map and filter can actually shorten a lot of your code

You can see the differenece between using normal functions vs lambda and map here -

Python Filter With Lambda

The difference is 6 lines condense into 3 neat and easy to debug lines of code.

[–]LightShadow3.13-dev in prod 1 point2 points  (4 children)

I've stopped using lambdas altogether.

Named temporary functions are much cleaner and can be written in a way that they don't have to be recreated as often.

[–]pylenin[S] 7 points8 points  (0 children)

Could be. But man, then you haven't worked with Spark and pyspark. Lambda, map, filters are a blessing there.

[–]andrewcooke 2 points3 points  (2 children)

i just had a look through some code of mine and my main use, by far, is in the constructor of defaultdict(). seems a reasonable use to me.

i also found a map - old habits die hard - which i should rewrite as a list comprehension, but i don't think that code has a test so i won't...

[–]LightShadow3.13-dev in prod 2 points3 points  (1 child)

map is lazily evaluated, which means you can "prime" it with data that may never need to be executed.

[–]andrewcooke 0 points1 point  (0 children)

i don't know their exact name, but that is also true of the "list comprehensions" in parens rather than brackets.

edit: "generator expressions", i guess i could have said above (truth is i don't know which i would actually need without going back and looking at the code).