Uso de la notación de comprensión de listas de Python

Negocio

En Python, es sencillo utilizar la notación de comprensión de listas al generar una nueva lista.(List comprehensions)

En este artículo, primero discutiremos lo siguiente

  • Tipo básico de notación de comprensión de listas
  • Notación de comprensión de listas con bifurcación condicional mediante if
  • Combinación con operadores ternarios (procesamiento tipo if else)
  • zip(),enumerate()La combinación con estos
  • notación de inclusión de listas anidadas

A continuación, explicaremos el conjunto de notación de comprensión de listas con un código de ejemplo.

  • notación de inclusión de conjuntos(Set comprehensions)
  • notación de inclusión en el diccionario(Dict comprehensions)
  • tipo de generador(Generator expressions)

Tipo básico de notación de comprensión de listas

La notación de comprensión de la lista se escribe como sigue.

[Expression for Any Variable Name in Iterable Object]

Toma cada elemento de un objeto iterable como una lista, tupla o rango por un nombre de variable arbitrario y lo evalúa con una expresión. Se devuelve una nueva lista con el resultado de la evaluación como elemento.

Se ofrece un ejemplo junto con una sentencia for equivalente.

squares = [i**2 for i in range(5)]
print(squares)
# [0, 1, 4, 9, 16]
squares = []
for i in range(5):
    squares.append(i**2)

print(squares)
# [0, 1, 4, 9, 16]

El mismo proceso puede realizarse con map(), pero se prefiere la notación de comprensión de listas por su simplicidad y claridad.

Notación de comprensión de listas con bifurcación condicional mediante if

También es posible la bifurcación condicional con if. Escriba el if en el postfijo como sigue.

[Expression for Any Variable Name in Iterable Object if Conditional Expression]

Sólo los elementos del objeto iterable cuya expresión condicional es verdadera son evaluados por la expresión, y se devuelve una nueva lista cuyos elementos son el resultado.

Puede utilizar cualquier nombre de variable en la expresión condicional.

Se ofrece un ejemplo junto con una sentencia for equivalente.

odds = [i for i in range(10) if i % 2 == 1]
print(odds)
# [1, 3, 5, 7, 9]
odds = []
for i in range(10):
    if i % 2 == 1:
        odds.append(i)

print(odds)
# [1, 3, 5, 7, 9]

El mismo proceso puede realizarse con filter(), pero se prefiere la notación de comprensión de listas por su simplicidad y claridad.

Combinación con operadores ternarios (procesamiento tipo if else)

En el ejemplo anterior, sólo se procesan los elementos que cumplen los criterios, y los que no los cumplen se excluyen de la nueva lista.

Si quieres cambiar el proceso dependiendo de la condición, o si quieres procesar los elementos que no satisfacen la condición de forma diferente, como en if else, utiliza el operador ternario.

En Python, el operador ternario puede escribirse como sigue

Value When True if Conditional Expression else Value When False

Se utiliza en la parte de expresión de la notación de comprensión de la lista, como se muestra a continuación.

[Value When True if Conditional Expression else Value When False for Any Variable Name in Iterable Object]

Se ofrece un ejemplo junto con una sentencia for equivalente.

odd_even = ['odd' if i % 2 == 1 else 'even' for i in range(10)]
print(odd_even)
# ['even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd']
odd_even = []
for i in range(10):
    if i % 2 == 1:
        odd_even.append('odd')
    else:
        odd_even.append('even')

print(odd_even)
# ['even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd']

También es posible escribir expresiones utilizando nombres de variables arbitrarios para los valores verdadero y falso.

Si la condición se cumple, se realiza algún tipo de procesamiento, de lo contrario, el valor del objeto iterable original se deja sin cambios.

odd10 = [i * 10 if i % 2 == 1 else i for i in range(10)]
print(odd10)
# [0, 10, 2, 30, 4, 50, 6, 70, 8, 90]

Combinación con zip() y enumerate()

Las funciones útiles que se utilizan a menudo en la sentencia for incluyen zip(), que combina múltiples iterables, y enumerate(), que devuelve un valor junto con su índice.

Por supuesto, es posible utilizar zip() y enumerar() con la notación de comprensión de listas. No es una sintaxis especial, y no es difícil si se tiene en cuenta la correspondencia con la sentencia for.

Ejemplo de zip().

l_str1 = ['a', 'b', 'c']
l_str2 = ['x', 'y', 'z']

l_zip = [(s1, s2) for s1, s2 in zip(l_str1, l_str2)]
print(l_zip)
# [('a', 'x'), ('b', 'y'), ('c', 'z')]
l_zip = []
for s1, s2 in zip(l_str1, l_str2):
    l_zip.append((s1, s2))

print(l_zip)
# [('a', 'x'), ('b', 'y'), ('c', 'z')]

Ejemplo de enumeración().

l_enu = [(i, s) for i, s in enumerate(l_str1)]
print(l_enu)
# [(0, 'a'), (1, 'b'), (2, 'c')]
l_enu = []
for i, s in enumerate(l_str1):
    l_enu.append((i, s))

print(l_enu)
# [(0, 'a'), (1, 'b'), (2, 'c')]

La idea es la misma que antes al utilizar if.

l_zip_if = [(s1, s2) for s1, s2 in zip(l_str1, l_str2) if s1 != 'b']
print(l_zip_if)
# [('a', 'x'), ('c', 'z')]

Cada elemento puede utilizarse también para calcular un nuevo elemento.

l_int1 = [1, 2, 3]
l_int2 = [10, 20, 30]

l_sub = [i2 - i1 for i1, i2 in zip(l_int1, l_int2)]
print(l_sub)
# [9, 18, 27]

notación de inclusión de listas anidadas

Al igual que los bucles for anidados, la notación de comprensión de listas también puede anidarse.

[Expression for Variable Name 1 in Iterable Object 1
    for Variable Name 2 in Iterable Object 2
        for Variable Name 3 in Iterable Object 3 ... ]

Por comodidad, se han añadido saltos de línea y sangrías, pero no son necesarios para la gramática; pueden continuar en una sola línea.

Se ofrece un ejemplo junto con una sentencia for equivalente.

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

flat = [x for row in matrix for x in row]
print(flat)
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
flat = []
for row in matrix:
    for x in row:
        flat.append(x)

print(flat)
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

También es posible utilizar múltiples variables.

cells = [(row, col) for row in range(3) for col in range(2)]
print(cells)
# [(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1)]

También se puede hacer una ramificación condicional.

cells = [(row, col) for row in range(3)
         for col in range(2) if col == row]
print(cells)
# [(0, 0), (1, 1)]

También es posible bifurcarse condicionalmente para cada objeto iterable.

cells = [(row, col) for row in range(3) if row % 2 == 0
         for col in range(2) if col % 2 == 0]
print(cells)
# [(0, 0), (2, 0)]

notación de inclusión de conjuntos(Set comprehensions)

El cambio de los corchetes [] en la notación de comprensión de la lista por las llaves {} crea un conjunto (objeto de tipo conjunto).

{Expression for Any Variable Name in Iterable Object}
s = {i**2 for i in range(5)}

print(s)
# {0, 1, 4, 9, 16}

notación de inclusión en el diccionario(Dict comprehensions)

Los diccionarios (objetos de tipo dict) también se pueden generar con la notación de comprensión.

{}, y especificar la clave y el valor en la parte de la expresión como clave: valor.

{Key: Value for Any Variable Name in Iterable Object}

Se puede especificar cualquier expresión para la clave y el valor.

l = ['Alice', 'Bob', 'Charlie']

d = {s: len(s) for s in l}
print(d)
# {'Alice': 5, 'Bob': 3, 'Charlie': 7}

Para crear un nuevo diccionario a partir de una lista de claves y valores, utilice la función zip().

keys = ['k1', 'k2', 'k3']
values = [1, 2, 3]

d = {k: v for k, v in zip(keys, values)}
print(d)
# {'k1': 1, 'k2': 2, 'k3': 3}

tipo de generador(Generator expressions)

Si los corchetes [] de la notación de comprensión de listas se utilizan como corchetes (), se devuelve un generador en lugar de una tupla. Esto se denomina expresiones generadoras.

Ejemplo de notación de comprensión de listas.

l = [i**2 for i in range(5)]

print(l)
# [0, 1, 4, 9, 16]

print(type(l))
# <class 'list'>

Ejemplo de expresión de un generador. Si imprimes() el generador tal cual, no imprimirá su contenido, pero si lo ejecutas con una sentencia for, puedes obtener el contenido.

g = (i**2 for i in range(5))

print(g)
# <generator object <genexpr> at 0x10af944f8>

print(type(g))
# <class 'generator'>

for i in g:
    print(i)
# 0
# 1
# 4
# 9
# 16

Las expresiones del generador también permiten la bifurcación y el anidamiento condicional utilizando la notación if, así como la notación de comprensión de listas.

g_cells = ((row, col) for row in range(0, 3)
           for col in range(0, 2) if col == row)

print(type(g_cells))
# <class 'generator'>

for i in g_cells:
    print(i)
# (0, 0)
# (1, 1)

Por ejemplo, si se genera una lista con un gran número de elementos utilizando la notación de comprensión de lista y luego se repite el bucle con una sentencia for, la lista que contiene todos los elementos se generará al principio si se utiliza la notación de comprensión de lista. En cambio, si se utiliza una expresión generadora, cada vez que se repite el bucle, los elementos se generan uno a uno, lo que reduce la cantidad de memoria utilizada.

Si la expresión del generador es el único argumento de la función, se pueden omitir los corchetes ().

print(sum([i**2 for i in range(5)]))
# 30

print(sum((i**2 for i in range(5))))
# 30

print(sum(i**2 for i in range(5)))
# 30

En cuanto a la velocidad de procesamiento, la notación de comprensión de la lista suele ser más rápida que la notación del generador cuando se procesan todos los elementos.

Sin embargo, cuando se juzga con all() o any(), por ejemplo, el resultado se determina cuando está presente false o true, por lo que usar expresiones generadoras puede ser más rápido que usar la notación de comprensión de listas.

No existe una notación de comprensión de tuplas, pero si se utiliza una expresión generadora como argumento de tuple(), se puede generar una tupla en la notación de comprensión.

t = tuple(i**2 for i in range(5))

print(t)
# (0, 1, 4, 9, 16)

print(type(t))
# <class 'tuple'>
Copied title and URL