Comprensión de listas

Santos Gallegos <santos_g@outlook.com>
@stsewd

Listas y Bucles

Listas

            
lista_numeros = [1, 2, 3]

lista_palabras = list("abcde")

lista_combinada = [1, "dos", 3.0]
            
          

Bucles

            
for numero in range(10):
    print(numero)

for numero in [1, 3, 5, 7]:
    print(numero)

for letra in "palabra":
    print(letra)
            
          

Ambos

            
numeros_pares = []
for numero in range(10):
    if numero % 2 == 0:
        numeros_pares.append(numero)
            
          

¿Qué es la comprensión de listas?

“Provee una forma concisa de crear listas. Una aplicación común es crear una nueva lista donde cada elemento es el resultado de aplicar alguna operación a cada miembro de otra secuencia...”

Documentación de Python

¿Cómo se ven?

          
numeros_pares = [numero for numero in range(10) if numero % 2 == 0]
          
        

Anatomía

Anatomía de compresión de listas
Tomado de: http://python-3-patterns-idioms-test.readthedocs.io/en/latest/Comprehensions.html

De bucles a comprensión de listas

“ Toda comprensión de lista puede ser escrita como un bucle for, pero no todo bucle for puede ser escrito como una comprensión de lista. ”

Trey Hunner

De bucles a comprensión de listas

            
pares_cuadrados = []
for numero in range(10):
    if numero % 2 == 0:
        pares_cuadrados.append(numero**2)
            
          

1. Copiar la variable que contendrá la nueva lista

            
pares_cuadrados = []
for numero in range(10):
    if numero % 2 == 0:
        pares_cuadrados.append(numero**2)

pares_cuadrados = []
            
          

2. Copiar lo que vamos a agregar a la nueva lista

            
pares_cuadrados = []
for numero in range(10):
    if numero % 2 == 0:
        pares_cuadrados.append(numero**2)

pares_cuadrados = [numero**2]
            
          

3. Copiar el bucle que recorre la secuencia

¡Sin los dos puntos!
            
pares_cuadrados = []
for numero in range(10):
    if numero % 2 == 0:
        pares_cuadrados.append(numero**2)

pares_cuadrados = [numero**2 for numero in range(10)]
            
          

4. Copiar el condicional

¡Sin los dos puntos!
            
pares_cuadrados = []
for numero in range(10):
    if numero % 2 == 0:
        pares_cuadrados.append(numero**2)

pares_cuadrados = [numero**2 for numero in range(10) if numero % 2 == 0]
            
          

Comprensión de listas más legibles

          
numeros_pares = [numero for numero in range(10) if numero % 2 == 0]

numeros_pares = [
    numero  # Nuevo valor
    for numero in range(10)  # Recorrer secuencia
    if numero % 2 == 0  # Condicional
]
          
        

Comprensión de listas y bucles anidados

          
matriz = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

matriz_aplanada = [
    elemento
    for fila in matriz
    for elemento in fila
]
          
        

Comprensión de listas vs map y filter

Mismo resultado, pero código más claro

          
numeros_pares = list(
    map(
        lambda n: n,
        filter(lambda numero: numero % 2 == 0, range(10))
    )
) 
          
        

Otras comprensiones

Comprensión de conjuntos (sets)

¡Nota los { }!
           
letras_unicas = set()
for letra in "palabra":
    letras_unicas.add(letra)

letras_unicas = {letra for letra in "palabra"}
           
          

Comprensión de diccionarios (dicts)

¡Nota los { } y los :!
           
numeros_letras = {
    1: "uno",
    2: "dos",
    3: "tres",
}

letras_numeros = {}
for numero, letra in numeros_letras.items():
    letras_numeros[letra] = numero

letras_numeros = {
    letra: numero
    for numero, letra in numeros_letras.items()
}
           
          

No siempre la solución más optima

  • A veces sólo necesitamos usar una lista una sóla vez.
  • Pasar una colección a otra función.
  • Procesar la misma colección varias veces.
          
            import timeit  

            # Comprensión de listas
            timeit.timeit("[n for n in range(10000)]", number=1000)

            # Expresión generadora
            timeit.timeit("(n for n in range(10000))", number=1000)
          
        

Expresiones generadoras

Aplican las mismas reglas que la comprensión de listas, pero se usan paréntesis. Sólo pueden ser usadas una vez.

Los paréntesis son opcionales si se usa dentro de una función.
          
            expresion_generadora_numeros_pares = (
                numero
                for numero in range(10)
                if numero % 2 == 0
            )

            " - ".join(
                str(numero)
                for numero in range(10)
                if numero % 2 == 0
            )
          
        

Scope de las variables

No hay efectos secundarios sobre nuestras variables.

          
            numero = 5
            for numero in range(10):
                pass

            print(numero)

            numero = 5
            numeros = [
                numero
                for numero in range(10)
            ]

            print(numero)
          
        

Recursos adicionales