El operador de expresión de asignación :=
agregado en Python 3.8 admite la asignación dentro de expresiones lambda. Este operador solo puede aparecer dentro de una expresión (...)
entre paréntesis , entre corchetes [...]
o entre corchetes {...}
por razones sintácticas. Por ejemplo, podremos escribir lo siguiente:
import sys
say_hello = lambda: (
message := "Hello world",
sys.stdout.write(message + "\n")
)[-1]
say_hello()
En Python 2, era posible realizar asignaciones locales como efecto secundario de las listas por comprensión.
import sys
say_hello = lambda: (
[None for message in ["Hello world"]],
sys.stdout.write(message + "\n")
)[-1]
say_hello()
Sin embargo, no es posible usar ninguno de estos en su ejemplo porque su variable flag
está en un alcance externo, no en el lambda
alcance de. Esto no tiene que ver con lambda
, es el comportamiento general en Python 2. Python 3 le permite evitar esto con la nonlocal
palabra clave dentro de def
s, pero nonlocal
no puede usarse dentro de lambda
s.
Hay una solución alternativa (ver más abajo), pero ya que estamos en el tema ...
En algunos casos, puede usar esto para hacer todo dentro de un 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()])()
Un cilindro con un radio de 10.0cm y una altura de 20.0cm tiene un volumen de 6283.2cm³.
Un cilindro con un radio de 20,0 cm y una altura de 40,0 cm tiene un volumen de 50265,5 cm³.
Un cilindro con un radio de 30.0cm y una altura de 60.0cm tiene un volumen de 169646.0cm³.
Por favor no lo hagas.
... volviendo a su ejemplo original: aunque no puede realizar asignaciones a la flag
variable en el alcance externo, puede usar funciones para modificar el valor asignado previamente.
Por ejemplo, flag
podría ser un objeto .value
que configuramos usando 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')]
Si quisiéramos encajar en el tema anterior, podríamos usar una lista de comprensión en lugar de setattr
:
[None for flag.value in [bool(o.name)]]
Pero realmente, en código serio, siempre debe usar una definición de función regular en lugar de una lambda
si va a realizar una asignación externa.
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)