python python-3.x list-comprehension python-assignment-expression python-3.8

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.