Tengo una lista de objetos y quiero eliminar todos los objetos que están vacíos excepto uno, usando filter
y una lambda
expresión.
Por ejemplo, si la entrada es:
[Object(name=""), Object(name="fake_name"), Object(name="")]
... entonces la salida debería ser:
[Object(name=""), Object(name="fake_name")]
¿Hay alguna forma de agregar una asignación a una lambda
expresión? Por ejemplo:
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
)
Respuestas:
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:En Python 2, era posible realizar asignaciones locales como efecto secundario de las listas por comprensión.
Sin embargo, no es posible usar ninguno de estos en su ejemplo porque su variable
flag
está en un alcance externo, no en ellambda
alcance de. Esto no tiene que ver conlambda
, es el comportamiento general en Python 2. Python 3 le permite evitar esto con lanonlocal
palabra clave dentro dedef
s, perononlocal
no puede usarse dentro delambda
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
: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 usandosetattr
:Si quisiéramos encajar en el tema anterior, podríamos usar una lista de comprensión en lugar de
setattr
: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.fuente
.setattr()
y similares (los diccionarios también deberían funcionar, por ejemplo) para piratear los efectos secundarios en el código funcional de todos modos, se mostró un código genial de @JeremyBanks :)assignment operator
!Realmente no puede mantener el estado en una expresión
filter
/lambda
(a menos que abuse del espacio de nombres global). Sin embargo, puede lograr algo similar utilizando el resultado acumulado que se transmite en unareduce()
expresión:Por supuesto, puede modificar un poco la condición. En este caso, filtra los duplicados, pero también puede usar
a.count("")
, por ejemplo, para restringir solo las cadenas vacías.No hace falta decir que puedes hacer esto, pero realmente no deberías. :)
Por último, puede hacer cualquier cosa en Python puro
lambda
: http://vanderwijk.info/blog/pure-lambda-calculus-python/fuente
No es necesario usar una lambda, cuando puede eliminar todas las nulas y volver a colocar una si cambia el tamaño de entrada:
fuente
output = [x for x in input if x.name]
.La asignación normal (
=
) no es posible dentro de unalambda
expresión, aunque es posible realizar varios trucos consetattr
y amigos.Sin embargo, resolver su problema es bastante simple:
que te dará
Como puede ver, mantiene la primera instancia en blanco en lugar de la última. Si necesita el último en su lugar, invierta la lista que va a
filter
, e invierta la lista que sale defilter
:que te dará
Una cosa a tener en cuenta: para que esto funcione con objetos arbitrarios, esos objetos deben implementarse correctamente
__eq__
y__hash__
como se explica aquí .fuente
ACTUALIZAR :
o usando
filter
ylambda
:Respuesta anterior
OK, ¿estás atascado en el uso de filtro y lambda?
Parece que esto se serviría mejor con una comprensión de diccionario,
Creo que la razón por la que Python no permite la asignación en una lambda es similar a la razón por la que no permite la asignación en una comprensión y eso tiene algo que ver con el hecho de que estas cosas se evalúan en un
C
lado y, por lo tanto, pueden darnos una aumento de velocidad. Al menos esa es mi impresión después de leer uno de los ensayos de Guido .Supongo que esto también iría en contra de la filosofía de tener una forma correcta de hacer cualquier cosa en Python.
fuente
TL; DR: cuando se usan modismos funcionales, es mejor escribir código funcional
Como muchas personas han señalado, en Python no se permite la asignación de lambdas. En general, cuando se utilizan modismos funcionales, es mejor pensar de manera funcional, lo que significa que, siempre que sea posible, no habrá efectos secundarios ni asignaciones.
Aquí hay una solución funcional que usa un lambda. He asignado la lambda a
fn
para mayor claridad (y porque se volvió un poco larga).También puede hacer este trato con iteradores en lugar de listas cambiando un poco las cosas. También tiene algunas importaciones diferentes.
Siempre puede reorganizar el código para reducir la longitud de las declaraciones.
fuente
Si en lugar de
flag = True
podemos hacer una importación, entonces creo que esto cumple con los criterios:O tal vez el filtro esté mejor escrito como:
O, solo para un booleano simple, sin ninguna importación:
fuente
La forma pitónica de rastrear el estado durante la iteración es con generadores. La forma de itertools es bastante difícil de entender en mi humilde opinión y tratar de hackear lambdas para hacer esto es una tontería. Intentaría:
En general, la legibilidad siempre triunfa sobre la compacidad.
fuente
No, no puede poner una asignación dentro de una lambda debido a su propia definición. Si trabaja con programación funcional, debe asumir que sus valores no son mutables.
Una solución sería el siguiente código:
fuente
Si necesita una lambda para recordar el estado entre llamadas, recomendaría una función declarada en el espacio de nombres local o una clase con una sobrecarga
__call__
. Ahora que todas mis advertencias contra lo que está tratando de hacer están fuera del camino, podemos obtener una respuesta real a su consulta.Si realmente necesita tener su lambda para tener algo de memoria entre llamadas, puede definirlo como:
Entonces solo necesitas pasar
f
afilter()
. Si realmente lo necesita, puede recuperar el valor deflag
con lo siguiente:Alternativamente, puede modificar el espacio de nombres global modificando el resultado de
globals()
. Desafortunadamente, no puede modificar el espacio de nombres local de la misma manera que modificar el resultado delocals()
no afecta el espacio de nombres local.fuente
(let ((var 42)) (lambda () (setf var 43)))
.Puede usar una función de vinculación para usar una lambda pseudo-múltiple. Luego, puede usar una clase contenedora para una bandera para habilitar la asignación.
fuente
Una especie de solución desordenada, pero la asignación en lambdas es ilegal de todos modos, por lo que realmente no importa. Puede usar la
exec()
función incorporada para ejecutar la asignación desde dentro de la lambda, como este ejemplo:fuente
Primero, no necesita usar una asignación local para su trabajo, solo verifique la respuesta anterior
segundo, es simple usar locals () y globals () para obtener la tabla de variables y luego cambiar el valor
verifique este código de muestra:
si necesita cambiar el agregar una variable global a su entorno, intente reemplazar locals () con globals ()
La composición de la lista de Python es genial, pero la mayoría del proyecto tridicional no acepta esto (como frasco: [)
espero que pueda ayudar
fuente
locals()
, dice explícitamente en la documentación que cambiarlo en realidad no cambia el alcance local (o al menos no siempre lo hará).globals()
por otro lado funciona como se esperaba.globals()
. pastebin.com/5Bjz1mR4 (probado en 2.6 y 3.2) lo demuestra.