Anidación con condiciones

Cuando anidamos varios bucles, sigue siendo posible aplicar condiciones a todos ellos. Por ejemplo, seguimos con el mismo ejemplo que acabamos de ver:

cities = ["Madrid", "Barcelona", "Milán", "Santander"]
years = [2017, 2018, 2019, 2020]

Si quisiéramos crear una lista de tuplas formadas por las combinaciones de ciudades que empiezan por la letra "M" y los años pares, podríamos hacerlo de la siguiente forma:

[(c, y) for c in cities for y in years if c.startswith("M") if y % 2 == 0]

[('Madrid', 2018), ('Madrid', 2020), ('Milán', 2018), ('Milán', 2020)]

Tal y como vemos, las condiciones se suceden una tras otra sin añadir ningún operador lógico entre ellas.

También podríamos haber combinado ambas condiciones en una sola:

[(c, y) for c in cities for y in years if (c.startswith("M")) & (y % 2 == 0)]

[('Madrid', 2018), ('Madrid', 2020), ('Milán', 2018), ('Milán', 2020)]

Las condiciones pueden "moverse" por la expresión siempre que hagan mención a una variable que ya haya sido declarada (si leemos la expresión de izquierda a derecha):

[(c, y) for c in cities if c.startswith("M") for y in years if y % 2 == 0]

[('Madrid', 2018), ('Madrid', 2020), ('Milán', 2018), ('Milán', 2020)]

En el anterior ejemplo obtendríamos un error si intercambiásemos las dos condiciones:

try:
    [(c, y) for c in cities if y % 2 == 0 for y in years if c.startswith("M")]
except:
    print("Error")

Error

...pues la condición "if y % 2 == 0" se estaría ejecutando antes de declarar la variable "y".