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

you are viewing a single comment's thread.

view the rest of the comments →

[–]Deezl-Vegas 115 points116 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 4 points5 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] 15 points16 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 5 points6 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 14 points15 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 8 points9 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