I have a list of objects and I want to remove all objects that are empty except for one, using filter
and a lambda
expression.
For example if the input is:
[Object(name=""), Object(name="fake_name"), Object(name="")]
...then the output should be:
[Object(name=""), Object(name="fake_name")]
Is there a way to add an assignment to a lambda
expression? For example:
flag = True input = [Object(name=""), Object(name="fake_name"), Object(name="")] output = filter( (lambda o: [flag or bool(o.name), flag = flag and bool(o.name)][0]), input )
Creating a Lambda Function The lambda operator cannot have any statements and it returns a function object that we can assign to any variable.
Python allows lambda nesting, i.e., you can create another lambda function inside a pre-existing lambda function. For nesting lambdas, you will need to define two lambda functions – an outer and an inner lambda function. When the outer lambda is called, the inner lambda creates a function.
Always use a def statement instead of an assignment statement that binds a lambda expression directly to a name. Assigning lambdas to names basically just duplicates the functionality of def - and in general, it's best to do something a single way to avoid confusion and increase clarity.
The assignment expression operator :=added in Python 3.8supports 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:
Python does not encourage using immediately invoked lambda expressions. It simply results from a lambda expression being callable, unlike the body of a normal function. Lambda functions are frequently used with higher-order functions, which take one or more functions as arguments or return one or more functions.
A lambda function is a small anonymous function. A lambda function can take any number of arguments, but can only have one expression. Add 10 to argument a, and return the result:
In this tutorial, you used assignment expressions to make compact sections of Python code that assign values to variables inside of if statements, while loops, and list comprehensions. For more information on other assignment expressions, you can view PEP 572 —the document that initially proposed adding assignment expressions to 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 sys say_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 sys say_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 def
s, but nonlocal
can't be used inside lambda
s.
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 result input = [Object(name=""), Object(name="fake_name"), Object(name="")] output = filter(not_empty_except_first, input)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With