Usando a notación de comprensións de listas de Python

Negocios

En Python, é sinxelo usar a notación de comprensión da lista cando se xera unha nova lista.(List comprehensions)

Neste artigo, primeiro comentaremos o seguinte

  • Tipo básico de notación de comprensión de listas
  • Listar a notación de comprensión con ramificación condicional por if
  • Combinación con operadores ternarios (se é o procesamento similar)
  • zip(),enumerate()Combinación con estes
  • notación de inclusión de listas aniñadas

A continuación, explicaremos o conxunto de notación de comprensión de listas con código de mostra.

  • establecer a notación de inclusión(Set comprehensions)
  • notación de inclusión de dicionario(Dict comprehensions)
  • tipo de xerador(Generator expressions)

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

A notación de comprensión da lista escríbese do seguinte xeito.

[Expression for Any Variable Name in Iterable Object]

Toma cada elemento dun obxecto iterable como unha lista, tupla ou rango por un nome de variable arbitrario e avalíao cunha expresión. Devólvese unha nova lista co resultado da avaliación como elemento.

Dáse un exemplo xunto cun equivalente para declaración.

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]

O mesmo proceso pódese facer con map(), pero a notación de comprensión da lista é preferida pola súa sinxeleza e claridade.

Listar a notación de comprensión con ramificación condicional por if

Tamén é posible a ramificación condicional con if. Escribe o se no postfixo do seguinte xeito.

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

Só se avalían pola expresión os elementos do obxecto iterable cuxa expresión condicional é verdadeira e devólvese unha nova lista cuxos elementos son o resultado.

Podes usar calquera nome de variable na expresión condicional.

Dáse un exemplo xunto cun equivalente para declaración.

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]

O mesmo proceso pódese facer con filter(), pero a notación de comprensión da lista é preferida pola súa sinxeleza e claridade.

Combinación con operadores ternarios (se é o procesamento similar)

No exemplo anterior, só se procesan aqueles elementos que cumpren os criterios e os que non cumpren os criterios quedan excluídos da nova lista.

Se queres cambiar o proceso dependendo da condición, ou se queres procesar elementos que non cumpren a condición de forma diferente, como en caso contrario, utiliza o operador ternario.

En Python, o operador ternario pódese escribir do seguinte xeito

Value When True if Conditional Expression else Value When False

Isto úsase na parte de expresión da notación de comprensión da lista como se mostra a continuación.

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

Dáse un exemplo xunto cun equivalente para declaración.

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']

Tamén é posible escribir expresións usando nomes de variables arbitrarias para os valores verdadeiro e falso.

Se se cumpre a condición, realízase algún procesamento; se non, o valor do obxecto iterable orixinal non se modifica.

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() e enumerate()

As funcións útiles que se usan a miúdo na instrución for inclúen zip(), que combina varios iterables, e enumerate(), que devolve un valor xunto co seu índice.

Por suposto, é posible usar zip() e enumerate() coa notación de comprensión de lista. Non é unha sintaxe especial, e non é difícil se ten en conta a correspondencia coa instrución for.

Exemplo 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')]

Exemplo de enumerate().

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')]

A idea é a mesma que antes cando se usa 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 tamén se pode usar para calcular un elemento novo.

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 aniñadas

Do mesmo xeito que o aniñamento de bucles, a notación de comprensión da lista tamén se pode aniñar.

[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 comodidade, engadíronse saltos de liña e sangrías, pero non son necesarios para a gramática; pódense continuar nunha soa liña.

Dáse un exemplo xunto cun equivalente para declaración.

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]

Tamén é posible utilizar varias 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)]

Tamén podes facer 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)]

Tamén é posible ramificar condicionalmente cada obxecto 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)]

establecer a notación de inclusión(Set comprehensions)

Cambiando os corchetes [] na notación de comprensión da lista por corchetes {} créase un conxunto (obxecto de tipo conxunto).

{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 de dicionario(Dict comprehensions)

Tamén se poden xerar dicionarios (obxectos de tipo dict) con notación de comprensión.

{} e especifique a clave e o valor na parte da expresión como clave: valor.

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

Pódese especificar calquera expresión para clave e valor.

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

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

Para crear un dicionario novo a partir dunha lista de claves e valores, use a 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 xerador(Generator expressions)

Se os corchetes [] na notación de comprensións da lista se usan como corchetes (), devólvese un xerador en lugar dunha tupla. Isto chámase expresións xeradoras.

Exemplo de notación de comprensión de lista.

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

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

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

Exemplo dunha expresión xeradora. Se imprimes () o xerador tal e como está, non imprimirá o seu contido, pero se o executas cunha instrución for, podes obter o contido.

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

As expresións xeradoras tamén permiten ramificación e anidación condicional usando if así como a notación de comprensión de lista.

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 exemplo, se se xera unha lista cun gran número de elementos mediante a notación de comprensión de lista e despois se realiza un bucle cunha instrución for, a lista que contén todos os elementos xerarase ao principio se se utiliza a notación de comprensión de lista. Por outra banda, se se utiliza unha expresión xeradora, cada vez que se repite o bucle, os elementos xéranse un a un, reducindo así a cantidade de memoria utilizada.

Se a expresión xeradora é o único argumento da función, pódense omitir os 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 canto á velocidade de procesamento, a notación de comprensión da lista adoita ser máis rápida que a notación do xerador cando se procesan todos os elementos.

Non obstante, ao xulgar con all() ou any(), por exemplo, o resultado determínase cando está presente falso ou verdadeiro, polo que usar expresións xeradoras pode ser máis rápido que usar a notación de comprensión de lista.

Non hai notación de comprensión de tuplas, pero se usas unha expresión xeradora como argumento de tupla(), podes xerar unha tupla na 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