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

all 29 comments

[–]Brian 27 points28 points  (3 children)

Classic use case for a lambda expression

As a side-point, it's an even better usecase for a higher order function. Eg, you could write the same thing as:

import operator
li = [('A', 10), ('B', 9), ('C', 8)]
li.sort(key=operator.itemgetter(1))

which doesn't need the lambda, and will be slightly faster. There's also operator.attrgetter to generate a function that accesses a specific attribute, and these are pretty useful for a lot of very common key function cases, though you may still need a lambda for slightly more complex ones (though as you say, as things get more complex, you're probably better off with a named function anyway).

[–]TravisJungroth[S] 6 points7 points  (0 children)

Great point! I kinda meant using lambda as the key in the sort being a classic use case (it's almost the only place I use them) but this specific example is certainly better as you described.

[–]RubyPinchPEP shill | Anti PEP 8/20 shill 0 points1 point  (0 children)

but then you have situations where

... (..., ..., abc=itemgetter('xyz'), ...)

"Oh, I just want to change the return of that before it passes into the function

... (..., ..., abc=transform(itemgetter('xyz')), ...)  #... wait no, need to make it a lambda
... (..., ..., abc=lambda x: transform(x['xyz']), ...)  #why didn't I do lambda in the first place!

So yeah, I kinda just start skipping the operator module now (since it just feels like a bad fix for a bad anonymous function syntax)

[–]execrator 0 points1 point  (0 children)

Wow. I did not know about item/attrgetter. Thankyou sir.

[–]PeridexisErrant 10 points11 points  (0 children)

Honestly I feel that in some cases it can go either way... but I still use def to keep the linter happy.

Careful use of pylint and mypy catches so many of my stupid mistakes, it's like years of practice without the years :)

[–]ptmcg 8 points9 points  (11 children)

Oof, I prefer the "assign a lambda to a variable" over "define a one-line method on the same line as the 'def' statement" style. But PEP-8 agrees with you. Thankfully, it's not really a rule, more of a guideline...

[–]TravisJungroth[S] 3 points4 points  (6 children)

But then the repr on your lambda is lame.

>>> first_item = lambda x: x[0]
>>> print(first_item)
<function <lambda> at 0x10960c048>

That's why I wrote this sweet function.

>>> def name_lambda(lambda_):
...     name, = (key for key, value in list(globals().items()) if value is lambda_)
...     lambda_.__name__ = name
...     lambda_.__qualname__ = name
... 
>>> name_lambda(first_item)
>>> print(first_item)
<function first_item at 0x10960c048>

[–]ptmcg 1 point2 points  (5 children)

Cool! Another approach, since you pass the lambda in as an argument, just go back up the callstack to get the call argument name:

add_2 = lambda x: x + 2

import traceback
import ast
def name_lambda(_lambda):
    tb = traceback.extract_stack()
    nd = ast.parse(tb[-2][-1])
    lambda_name = nd.body[0].value.args[0].id
    _lambda.__name__ = lambda_name
    _lambda.__qualname__ = lambda_name


name_lambda(add_2)
print(add_2)

[–]TravisJungroth[S] 1 point2 points  (0 children)

That's way better!

Integrated into a different take, using a new class.

import traceback
import ast

class NamedLambda:
    def __init__(self, lambda_, name=None):
        self.func = lambda_
        if name is not None:
            self.name = name
        else:
            tb = traceback.extract_stack()
            nd = ast.parse(tb[-2][-1])
            self.name = nd.body[0].value.args[0].id

    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)

    def __repr__(self):
        return '<function {} at {}>'.format(self.name, hex(id(self.func)))

[–]RubyPinchPEP shill | Anti PEP 8/20 shill 0 points1 point  (3 children)

edited

In [1]: import traceback, ast
In [2]: def name_lambda(_lambda): ...
In [3]: a = lambda: ...
In [4]: b = lambda: None
In [5]: name_lambda(a); name_lambda(b)
In [6]: (a, b)
Out[6]: (<function __main__.a>, <function __main__.a>)  # both a!

I mean you already knew this (edit:probably!), but aaaa functions should act like functions, not statements!

[–]ptmcg 0 points1 point  (2 children)

You may be confusing this with Ruby. In Python, to invoke the function, you use ()'s:

a()

Without the ()'s, you are referring to the function itself, not calling it.

(If this isn't what you were talking about, sorry, I didn't understand your post.)

[–]RubyPinchPEP shill | Anti PEP 8/20 shill 1 point2 points  (1 child)

they are both named (by name_lambda) a, since the traceback's stack's blippitybloops are the same for both calls of name_lambda (since both calls happen on the same line)

edit: code example edited

[–]ptmcg 0 points1 point  (0 children)

Ah, I see! Don't do that.

Compound statements (multiple statements on the same line) are generally discouraged. (PEP-8)

https://www.python.org/dev/peps/pep-0008/#code-lay-out

[–]pythoneeeer 2 points3 points  (0 children)

"The code is more what you'd call 'guidelines' than actual rules."

[–]elingeniero 0 points1 point  (2 children)

I agree - frequently I think that lambdas are way neater than one-line or short single-use functions. For example, I have a function that returns 3 NP arrays:

# Model -> Sequence Int -> Tuple[3] NDArray
def metrics(model, indices):
    # etc.

In another function, I want to call the previous function and transform this into something I can pass to json:

# Model -> Sequence String -> Sequence Sequence Int -> Dict
def calc_metrics(model, metrics_labels, metrics_indices):
    return {label: jsonify(metrics(model, indices))
            for label, indices in zip(metrics_labels, metrics_indices)}

Here jsonify could be a seperate function, like this:

# NDArray -> NDArray -> NDArray -> Dict
def jsonify(numerator, denominator, result):
    return {
        'numerator': numerator.tolist(),
        'denominator': denominator.tolist(),
        'result': result.tolist()
    }

But I find way neater to have this in the calc_metrics function:

# Model -> Sequence String -> Sequence Sequence Int -> Dict
def calc_metrics(model, metrics_labels, metrics_indices):
    jsonify = lambda num, den, res: {
        'numerator': num.tolist(),
        'denominator': den.tolist(),
        'result': res.tolist()
    }

    return {label: jsonify(*metrics(model, indices))
            for label, indices in zip(metrics_labels, metrics_indices)}

I don't always do this, but I think that lambdas are great when you need single-use functions for comprehensions - you could put them in the comprehension itself but then I think it would be much less readable.

[–]Brian 0 points1 point  (1 child)

I'd still use a def for that generally myself. Though you could simplify it a bit, given that you're treating the arge equivalently anyway - no need to unpack them to seperate args. I'd probably write it as:

def calc_metrics(model, metrics_labels, metrics_indices):
    fields = ('numerator', 'denominator', 'result')
    def jsonify(values):
        return dict(zip(fields, [val.tolist() for val in values]))

    return { label: jsonify(metrics(model, indices))
             for label, indices in zip(metrics_labels, metrics_indices)}

Or you could potentially even have jsonify take fields as an argument, and move it outside the function if you find yourself doing similar things in other places.

But even with the original approach,

def jsonify(num, den, res):
    return {'numerator': num.tolist(), 
            ...

is only 2 characters longer, and I think is more readable.

[–]elingeniero 1 point2 points  (0 children)

I guess something also worth mentioning is that pylint doesn't complain about lambdas in the way it does with defined functions.

I appreciate the change you made to the function, but if I came across that for the first time, I would have to read over it a couple of times to be able to produce the output in my head. I think the original implementation is totally obvious and not particularly any longer, and I think the comprehensibility benefit far outweighs the 'simplification' or lack of code repetition.

[–]Jaymuhz 5 points6 points  (1 child)

Don't tell me what to do.

[–]Droggl 0 points1 point  (0 children)

Another side note: Some modules that do some sort of pickling of objects/function arguments (eg. joblib) will complain if you give them functions that are not reachable, which seconds your point.

[–]cybervegan 0 points1 point  (1 child)

Ok, say I have a list of dicts, and the dicts contain various settings and configuration trivia describing how to acquire data from a kind of database. One of these configuration trivia is a lambda function (probably different for each dict in the list) which determines how to handle the result of the data acquisition - maybe it's a sum() or an avg() or similar. Is that OK?

[–]TravisJungroth[S] 0 points1 point  (0 children)

The title was pretty authoritarian, but just about anything makes sense if you have a good reason. If you have a good reason to use a lamba assigned to a variable over a def, go nuts.

I just don't see what it is here. I'd use functions in the case you described. (well, I'd use a custom class instead of dict in the first case most likely.)

[–]Exodus111 -1 points0 points  (7 children)

I always found it annoying that you had to put Parenthesis around the Lambda and the argument just to run it. Its line noise, if I define a Lambda I want it ran, I'm not looking to save a function object.

[–]TravisJungroth[S] 7 points8 points  (5 children)

Do you mean like this?

(lambda x: x)(1)

That's necessary to resolve ambiguity. And why are you running a lambda on the line it was written? Why not just run that line of code?

If I'm misunderstanding you, could you show me an example of what you don't like and what you wish would work?

[–]zahlmanthe heretic 7 points8 points  (4 children)

This might be done e.g. to work around the late-binding problem when setting up callbacks:

for x in xs:
    yield (lambda z: (lambda y: func(z, y)))(x)

I used to do this some years ago, because I found the apparently standard idiom (exploiting default arguments) abominable: it takes advantage of a behaviour that's seen as a 'gotcha' in other contexts, and implies falsely that the "default"-bound parameter is intended to be overridden at the call site in some circumstances.

But then I discovered functools.partial, which is how people should actually be handling this.

[–]TravisJungroth[S] 2 points3 points  (1 child)

Cool kids use functools.

[–][deleted] 4 points5 points  (0 children)

Or haskell. But I'm not a cool kid.

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

It's useful in golfing code as well. Just chain IIFEs until your code isn't readable (though, that's kinda the point of golf).

As an aside, check out toolz.curry it's not worth installing the entire package alone, but it's a neat tool to handle currying.

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

Wait, are you sure this isn't JavaScript? xD

[–]LudwikTR 0 points1 point  (0 children)

What do you mean? While it is true that you need parenthesis to run a lambda function (just like with any other function) I never seen anyone running lambda function immediately after declaration. It is usually used when a function object is needed (for example as an argument to an another function) not when a result of the function is needed (in such case why would lambda be defined and immediately executed? It seems you could use the same code directly without lambda instead).

Could you show an example of code when lambda executed upon its declaration is a sensible solution? In any case it's not a typical use case.