Buscar..


Introducción

Una lista de comprensión es una herramienta sintáctica para crear listas de forma natural y concisa, como se ilustra en el siguiente código para hacer una lista de cuadrados de los números del 1 al 10: [i ** 2 for i in range(1,11)] El dummy i de un range lista existente se usa para hacer un nuevo patrón de elemento. Se usa donde un bucle for sería necesario en lenguajes menos expresivos.

Sintaxis

  • [i for i in range (10)] # lista de comprensión básica
  • [i for i in xrange (10)] # lista de comprensión básica con objeto generador en Python 2.x
  • [i for i in range (20) if i% 2 == 0] # con filtro
  • [x + y para x en [1, 2, 3] para y en [3, 4, 5]] # bucles anidados
  • [i if i> 6 else 0 for i in range (10)] # expresión ternaria
  • [i if i> 4 else 0 para i en el rango (20) if i% 2 == 0] # con filtro y expresión ternaria
  • [[x + y para x en [1, 2, 3]] para y en [3, 4, 5]] # comprensión de lista anidada

Observaciones

La lista de comprensión se describió en PEP 202 y se introdujo en Python 2.0.

Lista de comprensiones condicionales

Dada una lista de comprensión , puede agregar uno o más if condiciones para filtrar los valores.

[<expression> for <element> in <iterable> if <condition>]

Para cada <element> en <iterable> ; Si <condition> evalúa como True , agregue <expression> (generalmente una función de <element> ) a la lista devuelta.


Por ejemplo, esto se puede usar para extraer solo números pares de una secuencia de enteros:

[x for x in range(10) if x % 2 == 0]
# Out: [0, 2, 4, 6, 8]

Demo en vivo

El código anterior es equivalente a:

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

print(even_numbers)
# Out: [0, 2, 4, 6, 8]

Además, una comprensión condicional de la lista de la forma [e for x in y if c] (donde e y c son expresiones en términos de x ) es equivalente a list(filter(lambda x: c, map(lambda x: e, y))) .

A pesar de proporcionar el mismo resultado, preste atención al hecho de que el ejemplo anterior es casi 2 veces más rápido que el segundo. Para aquellos que tienen curiosidad, esta es una buena explicación de la razón.


Tenga en cuenta que esto es bastante diferente de la expresión condicional ... if ... else ... (a veces conocida como expresión ternaria ) que puede usar para la parte <expression> de la lista de comprensión. Considere el siguiente ejemplo:

[x if x % 2 == 0 else None for x in range(10)]
# Out: [0, None, 2, None, 4, None, 6, None, 8, None]

Demo en vivo

Aquí, la expresión condicional no es un filtro, sino un operador que determina el valor que se utilizará para los elementos de la lista:

<value-if-condition-is-true> if <condition> else <value-if-condition-is-false>

Esto se vuelve más obvio si lo combinas con otros operadores:

[2 * (x if x % 2 == 0 else -1) + 1 for x in range(10)]
# Out: [1, -1, 5, -1, 9, -1, 13, -1, 17, -1]

Demo en vivo

Si está utilizando Python 2.7, xrange puede ser mejor que el range por varios motivos, como se describe en la documentación de xrange .

[2 * (x if x % 2 == 0 else -1) + 1 for x in xrange(10)]
# Out: [1, -1, 5, -1, 9, -1, 13, -1, 17, -1]

El código anterior es equivalente a:

numbers = []
for x in range(10):
    if x % 2 == 0:
        temp = x
    else:
        temp = -1
    numbers.append(2 * temp + 1)
print(numbers)
# Out: [1, -1, 5, -1, 9, -1, 13, -1, 17, -1]

Se puede combinar expresiones ternarios y if las condiciones. El operador ternario trabaja en el resultado filtrado:

[x if x > 2 else '*' for x in range(10) if x % 2 == 0]
# Out: ['*', '*', 4, 6, 8]

Lo mismo no podría haberse logrado solo por el operador ternario:

[x if (x > 2 and x % 2 == 0) else '*' for x in range(10)]
# Out:['*', '*', '*', '*', 4, '*', 6, '*', 8, '*']

Vea también: Filtros , que a menudo proporcionan una alternativa suficiente a las comprensiones de listas condicionales.

Lista de Comprensiones con Bucles Anidados

Las Comprensiones de lista pueden usar anidados for bucles. Puede codificar cualquier número de bucles for anidados dentro de una lista por comprensión, y cada uno for bucle puede tener una opción asociada if la prueba. Al hacerlo, el orden de la for las construcciones es del mismo orden que la hora de escribir una serie de anidado for declaraciones. La estructura general de las listas de comprensión se ve así:

[ expression for target1 in iterable1 [if condition1]
             for target2 in iterable2 [if condition2]...
             for targetN in iterableN [if conditionN] ]

Por ejemplo, el siguiente código aplanamiento una lista de listas utilizando múltiples for declaraciones:

data = [[1, 2], [3, 4], [5, 6]]
output = []
for each_list in data:
    for element in each_list:
        output.append(element)
print(output)
# Out: [1, 2, 3, 4, 5, 6]

se puede escribir de forma equivalente como una lista de comprensión con múltiples for construcciones:

data = [[1, 2], [3, 4], [5, 6]]
output = [element for each_list in data for element in each_list]
print(output)
# Out: [1, 2, 3, 4, 5, 6]

Demo en vivo

Tanto en la forma expandida como en la lista de comprensión, el bucle externo (primero para la declaración) aparece primero.


Además de ser más compacto, la comprensión anidada también es significativamente más rápida.

In [1]: data = [[1,2],[3,4],[5,6]]
In [2]: def f():
   ...:     output=[]
   ...:     for each_list in data:
   ...:         for element in each_list:
   ...:             output.append(element)
   ...:     return output
In [3]: timeit f()
1000000 loops, best of 3: 1.37 µs per loop
In [4]: timeit [inner for outer in data for inner in outer]
1000000 loops, best of 3: 632 ns per loop

La sobrecarga para la llamada de función anterior es de aproximadamente 140 ns .


En línea if s están anidados de manera similar, y puede ocurrir en cualquier posición después de la primera for :

data = [[1], [2, 3], [4, 5]]
output = [element for each_list in data
                if len(each_list) == 2
                for element in each_list
                if element != 5]
print(output)
# Out: [2, 3, 4]

Demo en vivo

Sin embargo, por razones de legibilidad, debe considerar el uso de bucles for tradicionales. Esto es especialmente cierto cuando el anidamiento tiene más de 2 niveles de profundidad y / o la lógica de la comprensión es demasiado compleja. la comprensión de múltiples listas de bucles anidadas podría ser propensa a errores o dar un resultado inesperado.

Refactorización de filtro y mapa para enumerar las comprensiones.

Las funciones de filter o map menudo deben ser reemplazadas por listas de comprensión . Guido Van Rossum describe esto bien en una carta abierta en 2005 :

filter(P, S) casi siempre se escribe más claro como [x for x in S if P(x)] , y esto tiene la gran ventaja de que los usos más comunes incluyen predicados que son comparaciones, por ejemplo, x==42 , y la definición de un lambda para eso solo requiere mucho más esfuerzo para el lector (más el lambda es más lento que la lista de comprensión). Más aún para el map(F, S) que se convierte en [F(x) for x in S] . Por supuesto, en muchos casos podrías usar expresiones generadoras.

Las siguientes líneas de código se consideran " no pythonic " y generarán errores en muchos linters de python.

filter(lambda x: x % 2 == 0, range(10)) # even numbers < 10
map(lambda x: 2*x, range(10)) # multiply each number by two
reduce(lambda x,y: x+y, range(10)) # sum of all elements in list

Tomando lo que hemos aprendido de la cita anterior, podemos desglosar estas expresiones de filter y map en sus listas de comprensión equivalentes; También elimina las funciones lambda de cada una, lo que hace que el código sea más legible en el proceso.

# Filter:
# P(x) = x % 2 == 0
# S = range(10)
[x for x in range(10) if x % 2 == 0]

# Map
# F(x) = 2*x
# S = range(10)
[2*x for x in range(10)]

La legibilidad se vuelve aún más evidente cuando se trata de funciones de encadenamiento. Donde debido a la legibilidad, los resultados de un mapa o función de filtro deben pasarse como resultado al siguiente; con casos simples, estos pueden ser reemplazados por una sola lista de comprensión. Además, podemos decir fácilmente de la comprensión de la lista cuál es el resultado de nuestro proceso, dónde hay más carga cognitiva al razonar sobre el proceso de Mapa y Filtro encadenado.

# Map & Filter
filtered = filter(lambda x: x % 2 == 0, range(10))
results = map(lambda x: 2*x, filtered)

# List comprehension
results = [2*x for x in range(10) if x % 2 == 0]

Refactorización - Referencia rápida

  • Mapa

    map(F, S) == [F(x) for x in S]
    
  • Filtrar

    filter(P, S) == [x for x in S if P(x)]
    

donde F y P son funciones que transforman respectivamente los valores de entrada y devuelven un bool

Comprensiones de lista anidadas

Las comprensiones de listas anidadas, a diferencia de las comprensiones de listas con bucles anidados, son comprensiones de listas dentro de una comprensión de listas. La expresión inicial puede ser cualquier expresión arbitraria, incluida otra lista de comprensión.

#List Comprehension with nested loop
[x + y for x in [1, 2, 3] for y in [3, 4, 5]]
#Out: [4, 5, 6, 5, 6, 7, 6, 7, 8]

#Nested List Comprehension
[[x + y for x in [1, 2, 3]] for y in [3, 4, 5]]
#Out: [[4, 5, 6], [5, 6, 7], [6, 7, 8]]

El ejemplo anidado es equivalente a

l = []
for y in [3, 4, 5]:
    temp = []
    for x in [1, 2, 3]:
        temp.append(x + y)
    l.append(temp)

Un ejemplo donde se puede usar una comprensión anidada para transponer una matriz.

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

[[row[i] for row in matrix] for i in range(len(matrix))]
# [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

Al igual que anidado for bucles, no hay límite a cómo se pueden anidar las comprensiones profundas.

[[[i + j + k for k in 'cd'] for j in 'ab'] for i in '12']
# Out: [[['1ac', '1ad'], ['1bc', '1bd']], [['2ac', '2ad'], ['2bc', '2bd']]]

Iterar dos o más listas simultáneamente dentro de la comprensión de la lista

Para iterar más de dos listas simultáneamente dentro de la comprensión de la lista , se puede usar zip() como:

>>> list_1 = [1, 2, 3 , 4]
>>> list_2 = ['a', 'b', 'c', 'd']
>>> list_3 = ['6', '7', '8', '9']

# Two lists
>>> [(i, j) for i, j in zip(list_1, list_2)]
[(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')]

# Three lists
>>> [(i, j, k) for i, j, k in zip(list_1, list_2, list_3)]
[(1, 'a', '6'), (2, 'b', '7'), (3, 'c', '8'), (4, 'd', '9')]

# so on ...


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow