Assignment inside lambda expression in Python Assignment inside lambda expression in Python python python

Assignment inside lambda expression in Python


The assignment expression operator := added in Python 3.8 supports assignment inside of lambda expressions. This operator can only appear within a parenthesized (...), bracketed [...], or braced {...} expression for syntactic reasons. For example, we will be able to write the following:

import syssay_hello = lambda: (    message := "Hello world",    sys.stdout.write(message + "\n"))[-1]say_hello()

In Python 2, it was possible to perform local assignments as a side effect of list comprehensions.

import syssay_hello = lambda: (    [None for message in ["Hello world"]],    sys.stdout.write(message + "\n"))[-1]say_hello()

However, it's not possible to use either of these in your example because your variable flag is in an outer scope, not the lambda's scope. This doesn't have to do with lambda, it's the general behaviour in Python 2. Python 3 lets you get around this with the nonlocal keyword inside of defs, but nonlocal can't be used inside lambdas.

There's a workaround (see below), but while we're on the topic...


In some cases you can use this to do everything inside of a lambda:

(lambda: [    ['def'        for sys in [__import__('sys')]        for math in [__import__('math')]        for sub in [lambda *vals: None]        for fun in [lambda *vals: vals[-1]]        for echo in [lambda *vals: sub(            sys.stdout.write(u" ".join(map(unicode, vals)) + u"\n"))]        for Cylinder in [type('Cylinder', (object,), dict(            __init__ = lambda self, radius, height: sub(                setattr(self, 'radius', radius),                setattr(self, 'height', height)),            volume = property(lambda self: fun(                ['def' for top_area in [math.pi * self.radius ** 2]],                self.height * top_area))))]        for main in [lambda: sub(            ['loop' for factor in [1, 2, 3] if sub(                ['def'                    for my_radius, my_height in [[10 * factor, 20 * factor]]                    for my_cylinder in [Cylinder(my_radius, my_height)]],                echo(u"A cylinder with a radius of %.1fcm and a height "                     u"of %.1fcm has a volume of %.1fcm³."                     % (my_radius, my_height, my_cylinder.volume)))])]],    main()])()

A cylinder with a radius of 10.0cm and a height of 20.0cm has a volume of 6283.2cm³.
A cylinder with a radius of 20.0cm and a height of 40.0cm has a volume of 50265.5cm³.
A cylinder with a radius of 30.0cm and a height of 60.0cm has a volume of 169646.0cm³.

Please don't.


...back to your original example: though you can't perform assignments to the flag variable in the outer scope, you can use functions to modify the previously-assigned value.

For example, flag could be an object whose .value we set using setattr:

flag = Object(value=True)input = [Object(name=''), Object(name='fake_name'), Object(name='')] output = filter(lambda o: [    flag.value or bool(o.name),    setattr(flag, 'value', flag.value and bool(o.name))][0], input)
[Object(name=''), Object(name='fake_name')]

If we wanted to fit the above theme, we could use a list comprehension instead of setattr:

    [None for flag.value in [bool(o.name)]]

But really, in serious code you should always use a regular function definition instead of a lambda if you're going to be doing outer assignment.

flag = Object(value=True)def not_empty_except_first(o):    result = flag.value or bool(o.name)    flag.value = flag.value and bool(o.name)    return resultinput = [Object(name=""), Object(name="fake_name"), Object(name="")] output = filter(not_empty_except_first, input)


You cannot really maintain state in a filter/lambda expression (unless abusing the global namespace). You can however achieve something similar using the accumulated result being passed around in a reduce() expression:

>>> f = lambda a, b: (a.append(b) or a) if (b not in a) else a>>> input = ["foo", u"", "bar", "", "", "x"]>>> reduce(f, input, [])['foo', u'', 'bar', 'x']>>> 

You can, of course, tweak the condition a bit. In this case it filters out duplicates, but you can also use a.count(""), for example, to only restrict empty strings.

Needless to say, you can do this but you really shouldn't. :)

Lastly, you can do anything in pure Python lambda: http://vanderwijk.info/blog/pure-lambda-calculus-python/


There's no need to use a lambda, when you can remove all the null ones, and put one back if the input size changes:

input = [Object(name=""), Object(name="fake_name"), Object(name="")] output = [x for x in input if x.name]if(len(input) != len(output)):    output.append(Object(name=""))