python - ¿Es posible agregar una cláusula where con comprensión de lista?
python-3.x list-comprehension (4)
Busca tener semántica-let en las comprensiones de la lista de python, cuyo alcance está disponible para la parte ___ for..in
(map) y if ___
(filtro), y cuyo alcance depende de la ... ..for ___ in...
Su solución, modificada: su (como usted admite ilegible) solución de [ (x,fx) for x,fx in ( (y,f(y) for y in iterable ) if fx ]
es la forma más directa de escribir la optimización .
Idea principal: levantar x en la tupla (x, f (x)).
Algunos argumentarían que la forma más "pitónica" de hacer las cosas sería la original [(x,f(x)) for x in iterable if f(x)]
y aceptar las ineficiencias.
Sin embargo, puede factorizar el ((y,fy) for y in iterable)
en una función, si planea hacer esto mucho. Esto es malo porque si alguna vez desea tener acceso a más variables que x,fx
(por ejemplo x,fx,ffx
), tendrá que volver a escribir todas las comprensiones de su lista. Por lo tanto, esta no es una gran solución a menos que sepa con seguridad que solo necesita x,fx
y un plan para reutilizar este patrón.
Expresión del generador:
Idea principal: use una alternativa más complicada a las expresiones generadoras: una en la que Python le permitirá escribir varias líneas.
Puedes usar una expresión generadora, con la que Python juega bien con:
def xfx(iterable):
for x in iterable:
fx = f(x)
if fx:
yield (x,fx)
xfx(exampleIterable)
Así es como lo haría personalmente.
Memorización / almacenamiento en caché:
Idea principal: también podría usar (¿abuso?) Efectos secundarios y hacer que f
tenga un caché de memoria global, para que no repita las operaciones.
Esto puede tener un poco de sobrecarga y requiere una política de cuán grande debe ser el caché y cuándo debe ser recolectada como basura. Por lo tanto, esto solo debe usarse si tendría otros usos para memorizar f, o si f es muy costoso. Pero te dejaría escribir ...
[ (x,f(x)) for x in iterable if f(x) ]
... como lo deseaba originalmente sin el impacto de rendimiento de hacer las operaciones caras en f
dos veces, incluso si técnicamente lo llama dos veces. Puede agregar un decorador @memoized
a f
: example (sin el tamaño máximo de caché). Esto funcionará siempre que x sea hashable (por ejemplo, un número, una tupla, un frozenset, etc.).
Valores ficticios:
Idea principal: capturar fx = f (x) en un cierre y modificar el comportamiento de la lista de comprensión.
filterTrue(
(lambda fx=f(x): (x,fx) if fx else None)() for x in iterable
)
donde filterTrue (iterable) es filter (Ninguno, iterable). Tendría que modificar esto si su tipo de lista (una tupla de 2) fuera realmente capaz de ser None
.
Considere la siguiente lista de comprensión
[ (x,f(x)) for x in iterable if f(x) ]
Esto filtra la condición basada en una condición f
y devuelve los pares de x,f(x)
. El problema con este enfoque es que f(x)
se calcula dos veces. Sería genial si pudiéramos escribir como
[ (x,fx) for x in iterable if fx where fx = f(x) ]
or
[ (x,fx) for x in iterable if fx with f(x) as fx ]
Pero en Python tenemos que escribir usando comprensiones anidadas para evitar la duplicación de llamadas a f (x) y hace que la comprensión parezca menos clara.
[ (x,fx) for x,fx in ( (y,f(y) for y in iterable ) if fx ]
¿Hay alguna otra manera de hacerlo más pitón y legible?
Actualizar
¡Próximamente en Python 3.8! PEP
# Share a subexpression between a comprehension filter clause and its output
filtered_data = [y for x in data if (y := f(x)) is not None]
Mapa y Zip?
fnRes = map(f, iterable)
[(x,fx) for x,fx in zip(iterable, fnRes) if fx)]
Nada dice que debes usar las comprensiones. De hecho, la mayoría de las guías de estilo que he visto solicitan que las limites a construcciones simples, de todos modos.
Podrías usar una expresión generadora, en su lugar.
def fun(iterable):
for x in iterable:
y = f(x)
if y:
yield x, y
print list(fun(iterable))
No hay ninguna instrucción where
, pero se puede "emular" usando for
:
a=[0]
def f(x):
a[0] += 1
return 2*x
print [ (x, y) for x in range(5) for y in [f(x)] if y != 2 ]
print "The function was executed %s times" % a[0]
Ejecución:
$ python 2.py
[(0, 0), (2, 4), (3, 6), (4, 8)]
The function was executed 5 times
Como puede ver, las funciones se ejecutan 5 veces, no 10 o 9.
Esto for
construcción:
for y in [f(x)]
Imitar donde la cláusula.