Usando a notação Python list comprehensions

O negócio

Em Python, é simples utilizar a notação de compreensões de listas ao gerar uma nova lista.(List comprehensions)

Neste artigo, discutiremos primeiro o seguinte

  • Tipo básico de notação de compreensão de listas
  • Notação de compreensão de lista com ramificação condicional por se
  • Combinação com operadores ternários (se outro tipo de processamento)
  • zip(),enumerate()Combinação com estes
  • notação de inclusão na lista aninhada

Em seguida, explicaremos o conjunto de notações de compreensão da lista com o código da amostra.

  • notação de inclusão definida(Set comprehensions)
  • notação de inclusão no dicionário(Dict comprehensions)
  • tipo de gerador(Generator expressions)

Tipo básico de notação de compreensão de listas

A notação de compreensão da lista é escrita da seguinte forma.

[Expression for Any Variable Name in Iterable Object]

Pega cada elemento de um objecto iterável tal como uma lista, tuple, ou intervalo por um nome variável arbitrário e avalia-o com uma expressão. Uma nova lista com o resultado da avaliação como um elemento é devolvida.

Um exemplo é dado juntamente com um equivalente para declaração.

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 processo pode ser feito com mapa(), mas a notação de compreensão da lista é preferida pela sua simplicidade e clareza.

Notação de compreensão de lista com ramificação condicional por se

Ramificação condicional com se também for possível. Escrever o if no postfix como se segue.

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

Apenas os elementos do objecto iterável cuja expressão condicional é verdadeira são avaliados pela expressão, e uma nova lista cujos elementos são o resultado é devolvida.

Pode usar qualquer nome de variável na expressão condicional.

Um exemplo é dado juntamente com um equivalente para declaração.

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 processo pode ser feito com filtro(), mas a notação de compreensão da lista é preferida pela sua simplicidade e clareza.

Combinação com operadores ternários (se outro tipo de processamento)

No exemplo acima, apenas os elementos que cumprem os critérios são processados, e os que não cumprem os critérios são excluídos da nova lista.

Se quiser mudar o processo dependendo da condição, ou se quiser processar elementos que não satisfaçam a condição de forma diferente, como se fosse de outra forma, utilize o operador ternário.

Em Python, o operador ternário pode ser escrito da seguinte forma

Value When True if Conditional Expression else Value When False

Isto é utilizado na parte de expressão da notação de compreensão da lista, como se mostra abaixo.

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

Um exemplo é dado juntamente com um equivalente para declaração.

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

Também é possível escrever expressões usando nomes de variáveis arbitrárias para os valores verdadeiros e falsos.

Se a condição for satisfeita, algum processamento é feito, caso contrário o valor do objecto original iterável é deixado inalterado.

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]

Combinação com zip() e enumerar()

As funções úteis que são frequentemente utilizadas na declaração para a declaração incluem zip(), que combina múltiplos iterables, e enumerar(), que retorna um valor juntamente com o seu índice.

Naturalmente, é possível usar zip() e enumerar() com notação de compreensão de lista. Não é uma sintaxe especial, e não é difícil se considerarmos a correspondência com o para declaração.

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 enumerar().

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 ideia é a mesma de antes quando se usa o 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 também pode ser utilizado para calcular um novo 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]

notação de inclusão na lista aninhada

Tal como o aninhamento para loops, a notação de compreensão de lista também pode ser aninhada.

[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 conveniência, foram acrescentadas quebras de linha e indentações, mas não são necessárias para a gramática; podem ser continuadas numa única linha.

Um exemplo é dado juntamente com um equivalente para declaração.

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]

Também é possível utilizar múltiplas variáveis.

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

Também se pode fazer ramificações condicionais.

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

Também é possível ramificar condicionalmente para cada objecto iterável.

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

notação de inclusão definida(Set comprehensions)

A alteração de parênteses rectos [] na notação de compreensão da lista para parênteses rectos {} cria um conjunto (objecto do tipo set-type).

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

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

notação de inclusão no dicionário(Dict comprehensions)

Os dicionários (objectos do tipo dict) também podem ser gerados com notação de compreensão.

{}, e especificar a chave e o valor na parte da expressão como chave: valor.

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

Qualquer expressão pode ser especificada para chave e valor.

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

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

Para criar um novo dicionário a partir de uma lista de chaves e valores, use a função 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 gerador(Generator expressions)

Se forem utilizados parênteses rectos [] na notação de compreensões da lista como parênteses redondos (), é devolvido um gerador em vez de um tuple. A isto chama-se expressões geradoras.

Exemplo de notação de compreensão de lista.

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

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

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

Exemplo de uma expressão geradora. Se imprimir() o gerador tal como está, ele não imprimirá o seu conteúdo, mas se o executar com uma declaração, poderá obter o conteúdo.

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 expressões de geradores também permitem a ramificação condicional e o agrupamento utilizando a notação de se, bem como a compreensão da 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 uma lista com um grande número de elementos for gerada utilizando a notação de compreensão de lista e depois ligada a uma declaração, a lista contendo todos os elementos será gerada no início se for utilizada a notação de compreensão de lista. Por outro lado, se for utilizada uma expressão geradora, cada vez que o laço é repetido, os elementos são gerados um a um, reduzindo assim a quantidade de memória utilizada.

Se a expressão do gerador for o único argumento da função, os parênteses redondos () podem ser omitidos.

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

Quanto à velocidade de processamento, a notação de compreensão da lista é frequentemente mais rápida do que a notação do gerador quando todos os elementos são processados.

No entanto, ao julgar com all() ou qualquer(), por exemplo, o resultado é determinado quando está presente falso ou verdadeiro, pelo que a utilização de expressões geradoras pode ser mais rápida do que a utilização de notação de compreensão de lista.

Não há notação de compreensão tuple, mas se usar uma expressão geradora como argumento de tuple(), pode gerar um tuple na notação de compreensão.

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