Python lista de comprensión con múltiples ‘si’

Todos sabemos python

[f(x) for x in y if g(x)] 

syntax.

Sin embargo, la representación AST de la lista de comprensión tiene espacio para más de una expresión ‘if’:

 comprehension = (expr target, expr iter, expr* ifs) 

¿Puede alguien darme un ejemplo de código de python que produzca un AST con más de una expresión ‘if’?

Apílalos uno tras otro:

 [i for i in range(100) if i > 10 if i < 50] 

Produce los enteros entre 11 y 49, inclusive.

La gramática permite múltiples declaraciones if porque puedes mezclarlas entre los bucles for:

 [j for i in range(100) if i > 10 for j in range(i) if j < 20] 

Los componentes de comprensión deben verse como declaraciones anidadas, lo anterior se traduce en:

 lst = [] for i in range(100): if i > 10: for j in range(i): if j < 20: lst.append(j) 

Esto también significa que puede usar varias declaraciones if sin for bucles intermedios:

 [i for i in range(100) if i > 10 if i < 20] 

Aunque no es sensato (solo combina aquellos que usan and o con operadores encadenados), se traduce en un conjunto de declaraciones anidadas legales:

 lst = [] for i in range(100): if i > 10: if i < 20: lst.append(i) 

La gramática y el analizador no rechazan específicamente dicho uso, de la misma manera que Python no le impide anidar las declaraciones if .

Tenga en cuenta que PEP 202 - List Comprehensions (el documento de propuesta original que agregó esta característica al idioma) en realidad incluye una doble comprensión en la sección de ejemplos:

 >>> print [(i, f) for i in nums for f in fruit if f[0] == "P" if i%2 == 1] [(1, 'Peaches'), (1, 'Pears'), (3, 'Peaches'), (3, 'Pears')] 

El uso de la incorporada all() permite colocar múltiples expresiones o funciones booleanas en un iterable y mantener su comprensión. Creo que es bastante subutilizado y se mantiene alta la facilidad de lectura.

 >>> [x for x in range(20) if all([1 < x < 10, not x & 1])] [2, 4, 6, 8] 

O

 >>> [x for x in range(20) if all([foo(x), bar(x)])] 

any() incorporado también funcionaría bien aquí si solo se necesita una condición para ser satisfecha:

 >>> [x for x in range(20) if any([1 < x < 10, not x & 1])] [0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18] 

La referencia del lenguaje da una mejor idea sobre esto:

 list_comprehension ::= expression list_for list_for ::= "for" target_list "in" old_expression_list [list_iter] list_iter ::= list_for | list_if list_if ::= "if" old_expression [list_iter] 

Como puede ver, la lista de comprensión se define con un list_iter opcional al final, un solo list_iter . Ahora este list_iter puede ser otra parte de la comprensión de la lista o una condición if. La condición if en sí misma termina nuevamente con otro list_iter opcional. Esto es esencial para que sea posible encadenar múltiples partes por partes con condiciones opcionales if en la misma lista de comprensión. El hecho de que también pueda construir un .. if X if Y if Z parte para list_iter es solo un efecto secundario.

Por lo tanto, aunque no es necesaria la posibilidad de encadenar múltiples condiciones if, esto permite que toda la gramática se defina de esa manera.