Idioma para aplanar una lista anidada poco profunda: ¿cómo funciona?

Encontré este bit de código en un módulo en el que estoy trabajando:

l = opaque_function() thingys = [x for y in l for x in y] 

No puedo leer esto. Por experimento, pude determinar que está aplanando una lista anidada de 2 niveles, pero el syntex todavía es opaco para mí. Obviamente ha omitido algunos paréntesis opcionales.

 >>> l = [[1,2],[3,4]] >>> [x for y in l for x in y] [1, 2, 3, 4] 

Mis ojos quieren analizarlo como: [x for y in [l for x in y] ] o [ [x for y in l] for x in y ] , pero ambos fallan debido a que y no está definido.

¿Cómo debo leer esto?

(Sospecho que me sentiré muy avergonzado cuando esto se explica).

De la lista se muestra la documentación:

Cuando se proporciona una lista de comprensión, consiste en una sola expresión seguida de al menos una for cláusula y cero o más for cláusulas o if las hay. En este caso, los elementos de la nueva lista son aquellos que se producirían considerando cada una de las cláusulas for o if un bloque, anidando de izquierda a derecha y evaluando la expresión para producir un elemento de lista cada vez que se alcanza el bloque más interno .

Por lo tanto, su expresión puede ser reescrita como:

 thingys = [] for y in l: for x in y: thingys.append(x) 

Esto solía confundirme realmente. Deberías leerlo como un bucle nested:

 new_list = [] for y in l: for x in y: new_list.append(x) 

se convierte en

 for y in l for x in y [do] new_list.append(x) 

se convierte en

 [x for y in l for x in y] 

Deberías leer esto como:

 for y in l: for x in y: yield x 

Esa es la versión del generador, pero todas las comprensiones tienen la misma syntax básica: mientras que la x se coloca al frente, el rest de la expresión aún se lee de izquierda a derecha. Esto también me confundió al principio, esperando que fuera al revés, pero tiene sentido una vez que agregue expresiones de filtrado:

 >>> l = [[1,2,3,4,5], [1,"foo","bar"], [2,3]] >>> [x for y in l ... if len(y) < 4 ... for x in y ... if isinstance(x, int)] [1, 2, 3] 

Ahora imagina tener que escribir todo esto al revés:

 [x if isinstance(x, int) for x in y if len(y) < 4 for y in l] 

Eso sería confuso incluso para los progtwigdores veteranos de Prolog, por no mencionar a las personas que mantienen analizadores de Python 🙂

La syntax actual también coincide con la de Haskell , que inspiró la comprensión de las listas en primer lugar.

 lis=[x for y in l for x in y] is Equivalent to: lis=[] for y in l: for x in y: lis.append(x)