Python: Compreensão de listas


Sobre tuplas e Zips

Algumas propriedades extras sobre tuplas são listadas aqui. Além disso usamos a fução zip() para unir objetos iteráveis de modo a poderem ser percorridos simultaneamente.

# Uma tupla não precisa de parenteses
» t = 1, 2, 3, 4
» t
↳ (1, 2, 3, 4)
» type(t)
↳ tuple
# podemos usar tuplas para declarações múltiplas
» x, y = 45, 78
» print(x,y, x+y)
↳ 45 78 123
# e percorrer uma tupla (ou lista) em um loop for
» t = [('a', 0, True), ('b', 1, False), ('c', 2, True)]
» for letra, num, bool in t:
»     if bool: print (num, letra)
↳ 0 a
↳ 2 c

# qualquer sequência pode ser usada no construtor de tuplas
» t = tuple('palavras')
» t
↳ ('p', 'a', 'l', 'a', 'v', 'r', 'a', 's')

# uma tupla de 1 elemento deve conter (,)
» t = ('a',)
» t
↳ ('a',)
» type(t)
↳ tuple

# tuplas para 'swap' de variáveis
» a = 'both'
» b = 'ambos'
print(a,b)
↳ both ambos
» a, b = b, a
» print(a,b)
↳ ambos both

Funções retornam uma variável ou None. No entanto essa variável pode ser uma tupla, um dicionário ou outro objeto qualquer. Dessa forma podemos ter funções que retornam mais de um valor. Por exemplo, divmod(a,b) retorna uma tupla com o resultado inteiro da divisão e o resto. Funções do usuário podem fazer o mesmo, inclusive retornando outra função, como veremos.

» divmod(10,3)
↳ (3, 1)

» div, resto = divmod(10,3)
» print(div)
↳ 3

» print(resto)
↳ 1

# A função abaixo recebe uma sequência e retorna uma tupla com seu mínimo e máximo
» def min_max(seq):
»     return min(seq), max(seq)
»
» seq = (23,45,23,78,1,23,0,-34)
» min_max(seq)
↳ (-34, 78)

» seq = {23,45,23,78,1,23,0,-34}
» min_max(seq)
↳ (-34, 78)

# funções com n argumentos
» def imprima_tudo(*args):
»     print(args)

» imprima_tudo(1,2, '3', True)
↳ (1, 2, '3', True)

# sem * temos um erro
» def imprima(args):
»     print(args)
» imprima(1,2)
↳
---------------------------------------------------------------------------
TypeError: imprima() takes 1 positional argument but 2 were given

Função zip()

Uma função interna interessante e útil é zip(). Ela recebe como argumentos duas ou mais sequências e as compacta em um objeto zip que é um iterador de tuplas, onde cada tupla contem um elemento de cada sequência usada como parâmetro. No objeto retornado a i-ésima tupla contém o i-ésimo elemento de cada uma das sequências ou iteráveis fornecidos em seu argumento. Se as sequências não tem o mesmo comprimento o iterador retornado tem o comprimento da menor. Se nenhum argumento for fornecido ela retorna um iterador vazio. Com um único argumento iterável, ele retorna um iterador de tuplas de um elemento.

» seq1 = 'Blade'
» seq2 = 'Runner'
» zipado = zip(seq1, seq2)
» print(zipado)
↳ <zip object at 0x7f27c9f77500>

# Para visualizar esse objeto zip (um iterador) podemos transform-a-lo em uma tupla
» print(tuple(zipado))
↳ (('B', 'R'), ('l', 'u'), ('a', 'n'), ('d', 'n'), ('e', 'e'))
# ou percorrê-lo em seus elementos
» for letra1, letra2 in zipado:
»     print(letra1, letra2)
↳ B R
↳ l u
↳ a n
↳ d n
↳ e e

# um objeto zip pode ser passado como argumento na construção
# de uma lista gerando uma lista de tuplas
» s1 = [1, 2, 3]
» s2 = ['a', 'b', 'c']
» list(zip(s1, s2))
↳ [(1, 'a'), (2, 'b'), (3, 'c')]

# várias sequências podem ser fornecidas
» s1 = 'Margem'
» s2 = 'direita'
» s3 = 'do rio'
» zip123 = zip(s1, s2, s3)
» for a,b,c in zip123:
»     print(a, b, c)
↳ M d d
↳ a i o
↳ r r
↳ g e r
↳ e i i
↳ m t o

No útimo exemplo a sequência s2 tem maior comprimento que as demais. A letra final ‘a’ foi ignorada.

Suponha que desejamos analisar duas sequências e verificar se elas possuem elementos iguais em uma mesma posição. No exemplo abaixo fazemos este teste usando zip() e definindo uma função que retorna True se houver uma coincidência. Observe que a função é abandonada quando ocorre a primeira coincidência.

Pode ocorrer que precisamos saber qual é a posição desse (ou de vários elementos comuns). Para isso usamos enumerate(). Ela tem a seguinte forma:

enumerate(iteravel, inicio=0)

onde iteravel é uma sequência ou qualquer objeto iterável. Ela retorna pares iteráveis contendo o elemento e sua posição, começando em início (com default 0) e o elemento da sequência.

# função que retorna True se houver coincidência de elementos (ou False se não houver)
» def mesmo_elemento(t1, t2):
» for i, j in zip(t1, t2):
»     if i == j:
»         return True
» return False

» t1 = [1,2,3,4,5,6,7,8,9]
» t2 = [9,8,7,6,5,4,3,2,1]
» mesmo_elemento(t1,t2)
↳ True

# Para informar a posição da coincidência usamos enumerate
» for index, element in enumerate('abc'):
»     print(index, element)
↳ 0 a
↳ 1 b
↳ 2 c

» type(enumerate('abdc'))
↳ enumerate

# Repetindo o exemplo anterior, queremos descobrir se duas sequências
# possuem elementos comuns na mesma posição, e que posição é essa

» def posicao_elemento(t1, t2):
»     ''' retorna dictionary com indice e elemento onde elemento de t1 é igual ao de t2 '''
»     dict = {}
»     if len(t2) < len(t1):
»         (t1, t2) = (t2, t1)
»     for index, i in enumerate(t1):
»         if i == t2[index]:
»             dict[index] = i
»     return dict

» t1 = [9,8,7,4,3,4,5,6,7,8,9,0,23,45]
» t2 = [1,2,3,4,5,6,7,6,9]
» posicao_elemento(t1,t2)
↳ {3: 4, 7: 6}

» t1 = 'josefina'
» t2 = 'gasolina cara'
» posicao_elemento(t1,t2)
↳ {2: 's', 5: 'i', 6: 'n', 7: 'a'}

As linhas if len(t2) < len(t1): (t1, t2) = (t2, t1) garante que t2 tenha o maior comprimento. Caso contrário o teste i == t2[index] poderia poderia resultar em erro ao tentar obter elemento inexistente de t2.

Compreensão de Listas (List Comprehensions)


Suponha que queremos uma lista com os quadrados de todos os números de 0 até 10. Podemos conseguir isso usando um laço for:

» quadrados = []
» for i in range(11):
»     quadrados.append(i**2)
»
» quadrados
↳ [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

Há uma forma alternativa no Python para conseguir o mesmo resultado chamada compreensão de listas (list comprehensions). Sua sintaxe básica é

[expressão(item) for item in lista if condição].

Com isso conseguimos o mesmo resultado acima de forma mais compacta:

» quads = [i**2 for i in range(11)]
» quads
↳ [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

Essa forma permite que se escreva um código mais compacto, legível e eficiente (portanto de mais rápida execução). A expressão pode ser uma função do usuário. No exemplo usamos a função já definida anteriormente para o cálculo de fatoriais:

» def fatorial(n):
»     if n <= 1: return 1
»     else: return n*fatorial(n-1)
»
# exibindo os fatoriais de ímpares de 1 a 9
» fats = [fatorial(i) for i in [1, 3, 5, 7, 9]]
» fats
↳ [1, 6, 120, 5040, 362880]

# alternativamente, usando a condicional
» ft = [fatorial(i) for i in range(10) if i %2 ==1]
» ft
↳ [1, 6, 120, 5040, 362880]

# atuando sobre strings
» palavras = ['Organização', 'Nações', 'Unidas']
» letras = [l[0] for l in palavras]
» letras
↳ ['O', 'N', 'U']

» tamanho = [len(p) for p in palavras]
» tamanho
↳ [11, 6, 6]

Uma ou mais condições pode ser incluída na lista a ser iterada.

[expressao(item) for item in lista if condição1 if condição2 ...]

No exemplo abaixo usamos i%2, que é o resto da divisão de i por 2. Se esse resto for 0 o número é par.

# ex.: lista dos números pares de 0 até 10, exclusive
# i%2 == 0 significa que o número é par
» num = [i for i in range(10) if i%2==0 ]
» print(num)
↳ [0, 2, 4, 6, 8]

# um exemplo com strings: apenas as palavras com comprimento maior que 5
» palavras = ['casa', 'aleatório', 'rudimentar', 'longo']
» [p for p in palavras if len(p) > 5 ]
↳ ['aleatório', 'rudimentar']

# Várias condições podem ser impostas simultaneamente
# São retornados os múltiplos de 2 e 3 simultaneamente, i.e. os múltiplos de 6.
» print([i for i in range(50) if i%2==0 if i%3==0])
↳ [0, 6, 12, 18, 24, 30, 36, 42, 48]

# a expressão na lista pode conter 'if else':
# a abaixo expressão retorna 'abacaxi' para números pares,
# 'laranja' para múltiplos de 3 e 'caqui' para todos os demais
» fruits = ['abacaxi' if i%2==0 else 'laranja' if i%3==0 else 'caqui' for i in range(10)]
» print(fruits)
↳ ['abacaxi', 'caqui', 'abacaxi', 'laranja', 'abacaxi', 'caqui', 'abacaxi', 'caqui', 'abacaxi', 'laranja']

# As listas podem ser aninhadas. Abaixo a lista externa percorre letras,
# a lista interna percorre números
» matriz = [[i+j for i in 'abcd'] for j in '1234']
» matriz
↳ [['a1', 'b1', 'c1', 'd1'],
   ['a2', 'b2', 'c2', 'd2'],
   ['a3', 'b3', 'c3', 'd3'],
   ['a4', 'b4', 'c4', 'd4']]

Um teste pode ser negado com o operador lógico not (que inverte o booleano). Usamos também o método string.isalpha() que retorna True se string for formado apenas de caracteres (sem dígitos ou pontuações).

# teste not in
# isalpha() retorna True se a string é composta de caracteres alfabéticos
» vogais = {'a', 'e', 'i', 'o', 'u'}
» texto = 'Aprenda todas as regras e transgrida algumas 1234.'
» letras = set(texto.lower())
» consoantes = {letra for letra in letras if letra not in vogais if letra.isalpha()}
» consoantes
↳ {'d', 'g', 'l', 'm', 'n', 'p', 'r', 's', 't'}

# na expressão seguinte um conjunto (set) de tuplas é gerado
{(i, j) for i in range(3, 5) for j in range(2)}
↳ {(3, 0), (3, 1), (4, 0), (4, 1)}

» print([i+j for i in 'abcd' for j in 'efgh'])
↳ ['ae', 'af', 'ag', 'ah', 'be', 'bf', 'bg', 'bh', 'ce', 'cf', 'cg', 'ch', 'de', 'df', 'dg', 'dh']

» substantivo = ['pão', 'pé', 'carro', 'bolo', 'mato']
» adjetivo = ['pequeno', 'bonito', 'bom', 'caro']
» ['%s %s' % (s, a) for s in substantivo for a in adjetivo if s[0] == a[0]]
↳ ['pão pequeno', 'pé pequeno', 'carro caro', 'bolo bonito', 'bolo bom']

# Podemos calcular o produto escalar de 2 vetores
» u = [1,3,4,5,6]
» v = [-1,0,9,-3,2]
» sum([a+b for a,b in zip(u,v)])
↳ 26

No cálculo do produto escalar zip(u,v) contém 5 pares que são somados formando uma lista de 5 elementos. A função sum(lista) soma esses 5 elementos.

Compreensão com dicionários

Compreensão de listas podem ser aplicadas a dicionários, alterando tanto suas chaves como valores. Lembrando dic.items() retorna uma tupla chave:valor do dicionário.

# uma compreensão para duplicar os valores em um dicionário
» dic1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
» duplo_dic1 = {k:v*2 for (k,v) in dic1.items()}
» duplo_dic1
↳ {'a': 2, 'b': 4, 'c': 6, 'd': 8, 'e': 10}

# alterar o texto nas chaves, acrescentando prefixo R
» R_dic1 = {'R'+k : v*2 for (k,v) in dic1.items()}
» R_dic1
↳ {'Ra': 2, 'Rb': 4, 'Rc': 6, 'Rd': 8, 'Re': 10}

» s_dic = {l.upper(): l*3 for l in 'dna'}
» print (s_dic)
↳ {'D': 'ddd', 'N': 'nnn', 'A': 'aaa'}

No exemplo seguinte queremos construir um dicionário onde a chave é um par entre 1 e 10, inclusive, e o valor é seu quadrado. Fazemos isso sem compreensão de lista e depois com.

# Obs. percorrer i = 1, ..., 5 e tomar seu dobro garante que usamos apenas os pares de 1 a 10
» dic_2 = {}
» for i in range(1,6):
»     dic_2[i*2] = (i*2)**2
» dic_2
↳ {2: 4, 4: 16, 6: 36, 8: 64, 10: 100}

# a mesma construção, usando compreensão
» dic_3 = {n:n**2 for n in range(1,11) if n%2 == 0}
» dic_3
↳ {2: 4, 4: 16, 6: 36, 8: 64, 10: 100}

# o mesmo resultado será obtido com
» dic_4 = {2*n:(2*n)**2 for n in range(1,6)}
» dic_4
↳ {2: 4, 4: 16, 6: 36, 8: 64, 10: 100}

# Podemos usar compreensões para filtrar elementos de um dicionário
» dic_4a = {k:v for (k,v) in dic_4.items() if v>36}
» dic_4a
↳ {8: 64, 10: 100}

» # selecionando apenas valores em um intervalo (2 condições)
» dic_4b = {k:v for (k,v) in dic_4.items() if v > 16 if v < 100}
» dic_4b
↳ {6: 36, 8: 64}

# duas condições, sobre chave e valor
» dic_4c = {k:v for (k,v) in dic_4.items() if k > 5 if v < 90}
» dic_4c
↳ {6: 36, 8: 64}

# o valor é (im)par para chave (im)par
» dic_par_impar = {i:('par' if i%2==0 else 'impar') for i in range(5)}
» dic_par_impar
↳ {0: 'par', 1: 'impar', 2: 'par', 3: 'impar', 4: 'par'}

Algumas vezes pode ser útil usar zip() para construir dicionários através de uma compreensão.

# Lists to represent keys and values
» keys = [101, 201, 301, 401, 501]
» values = ['inglês', 'francês', 'alemão', 'russo', 'espanhol']

# keys e values são colocados em um único iterável (um objeto zip)
» dic_5 = { k:v for (k,v) in zip(keys, values)}
» print(dic_5)
↳ {101: 'inglês', 201: 'francês', 301: 'alemão', 401: 'russo', 501: 'espanhol'}

# observe que o iterável poderia ser dado diretamente como parâmetro do construtor
» dic_6 = dict(zip(keys, values))
» print(dic_6)
↳ {101: 'inglês', 201: 'francês', 301: 'alemão', 401: 'russo', 501: 'espanhol'}

# dicionários aninhados
» dicionario = {i: {j: i*j for j in range(1, 6)} for i in range(2, 5)}
» print(dicionario)
↳ {2: {1: 2, 2: 4, 3: 6, 4: 8, 5: 10}, 3: {1: 3, 2: 6, 3: 9, 4: 12, 5: 15}, 4: {1: 4, 2: 8, 3: 12, 4: 16, 5: 20}}
# Nesse dicionário as chaves vão de 2 a 4 e seus valores são outros dicionários.

Compreensões de listas e de dicionários são formas elegantes e de fácil leitura. No entanto, se forem muito complexas elas podem tornar o código difícil de ler e de debugar (encontrar eventuais erros). Devemos nos lembrar que outras pessoas podem necessitar ler o nosso código, ou você mesmo, daqui a um tempo, quando dificilmente se lembrará do raciocínio que te levou à construção de bloco sofisticados e elegantes.

Função sorted()

A função

sorted(iteravel, key, reverse)

é usada para ordenar iteráveis (sequências ou coleções). Os parâmetros key e reverse são opcionais. keyé uma funcão comparadora de elementos usada para estabelecer a ordem usada e reverse = True promove a ordenação reversa. O default é False. A função retorna uma lista de elementos ordenados.

» a = ("bola", "almeja", "cara", "fraco", "dado", "errado", "gato")
» print(sorted(a))
↳ ['almeja', 'bola', 'cara', 'dado', 'errado', 'fraco', 'gato']

» print(sorted(a, reverse=True))
↳ ['gato', 'fraco', 'errado', 'dado', 'cara', 'bola', 'almeja']

# uma string é uma sequência
» x = "intrinsecamente"
» print(sorted(x))
↳ ['a', 'c', 'e', 'e', 'e', 'i', 'i', 'm', 'n', 'n', 'n', 'r', 's', 't', 't']

# um dictionário tem as suas chaves ordenadas
» x = {'p': 1, 'y': 2, 't': 3, 'h': 4, 'o': 5, 'n': 6}
» print(sorted(x))
↳ ['h', 'n', 'o', 'p', 't', 'y']

# os elementos de um conjunto podem ser ordenados
» x = {7, 1, 2, 9, 3, 5}
» print(sorted(x))
↳ [1, 2, 3, 5, 7, 9]

# a função interna len pode ser usada como critério de ordenação
» palavras = ['a', 'casa', 'da', 'mãe', 'joana', 'foi', 'atacada', 'por', 'soldados' ]
» print(sorted(palavras, key=len))
↳ ['a', 'da', 'mãe', 'foi', 'por', 'casa', 'joana', 'atacada', 'soldados']

Outras funções podem ser usadas, inclusive funções definidas pelo usuário. No exemplo abaixo a função ordenadora substitui o valor na lista por seu resto na divisão por nove.

# ordena lista de inteiros baseado em seu resto na divisão por 9
» def func(x):
»     return x % 9

» L = [9, 15, 31, 111, 7]

» print("Ordenação normal :", sorted(L))
» print("Ordenação baseada no resto por 9:", sorted(L, key=func))

↳ Ordenação normal : [7, 9, 15, 31, 111]
↳ Ordenação baseada no resto por 9: [9, 111, 31, 15, 7]

Função filter()


A função tem a forma de

filter(funcao, sequencia)

e constroi um iterador usando uma função booleana (que retorna True ou False) e uma sequência ou um iterável qualquer. Ela testa cada elemento da sequência usando a função e retorna apenas os elementos avaliados como True.

» palavras = ['ciência', 'latim', 'conhecimento', 'prática', 'sistemática', 'pesquisa']
# menores é True se a palavra tiver até 7 letras
» def menores(palavra):
»     if len(palavra) < 8:
»         return True
»     else:
»         return False

» menores_palavras = filter(menores, palavras)
» for m in menores_palavras:
»     print(m)
↳ ciência
↳ latim
↳ prática

# observe que a função poderia ser mais compacta:
» def menores(palavra):
»     return len(palavra) < 8

No próximo exemplo temos uma lista de dicionários. Cada entrada dessa lista é um dicionário contendo dados sobre um animal. O código define uma função pesquisa que recebe essa lista e procura quais de suas entradas contém algum valor com o substring busca.

» bicharada = [
»   {'nome': 'Asdrubal', 'especie': 'tubarão', 'peso': '290', 'habitat': 'oceano'},
»   {'nome': 'Ana Lee', 'especie': 'caranquejo', 'peso': '1.2', 'habitat': 'oceano'},
»   {'nome': 'Raul', 'especie': 'urso', 'peso': '180', 'habitat': 'floresta'},
»   {'nome': 'Joana', 'especie': 'raposa', 'peso': '21', 'habitat': 'floresta'},
»   {'nome': 'Omar Lee', 'especie': 'golfinho', 'peso': '120', 'habitat': 'oceano'},
»   {'nome': 'Tulio', 'especie': 'rato', 'peso': '1.4', 'habitat': 'doméstico'}
» ]

» def pesquisa(lista, busca):
»     def criterio(x):
»         for v in x.values():
»             if busca in v:
»                 return True
»         return False
»     return filter(criterio, lista)

» lista_filtrada = pesquisa(bicharada, 'tuba')
» for t in lista_filtrada:
»     print(t)
↳ {'nome': 'Asdrubal', 'especie': 'tubarão', 'peso': '290', 'habitat': 'oceano'}

» lista_filtrada = pesquisa(bicharada, 'Lee')
» for t in lista_filtrada:
»     print(t)
↳ {'nome': 'Ana Lee', 'especie': 'caranquejo', 'peso': '1.2', 'habitat': 'oceano'}
↳ {'nome': 'Omar Lee', 'especie': 'golfinho', 'peso': '120', 'habitat': 'oceano'}

Sugestão: altere o código acima para encontrar parte de um valor dentro de um campo especificado. Encontre o animal cujo habitat contém a substring 'ocea'.

Iteradores (iterators)

No Python um iterador é um objeto que contém um número contável de elementos que podem ser iterados, ou seja, que podem ser acessados e lidos um de cada vez. Além dos laços for e das compreensões de listas e sequências no Python podemos utilizar iteradores para percorrer sequências. Eles pode ser construídos com a função

nome_iterador = iter(sequencia, marcador).

O parâmetro marcador é opcional. O iterador possui o método _next()_ que fornece o próximo elemento. Uma exceção StopIteration é lançada no final, quando todos os elementos foram percorridos. O iterador é esgotado e, portanto, só pode ser percorrido uma vez.

Exemplos de uso estão no código abaixo. Listas são convertidas em iteradores, que são percorridos com next().
Se a iteração não é interrompida antes do fim um erro é lançado. Um erro é gerado no final e sua captura é usada para interromper o laço.

# Uma lista é convertida em um iterador
» lista = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
» it = iter(lista)
» for k in range(len(lista)):
»     print(next(it), end=' ')
↳ a b c d e f g h

# queremos separar números positivos e negativos de uma lista
# o erro no final da iteração é usado para interromper o laço
» original = [1, 6, -8, -3, 0, -2, 9, 76, -45]
» positivos = []
» negativos = []
» t = iter(original)
» try:
»     while True:
»         x = next(t)
»         if x >= 0:
»             positivos.append(x)
»         else:
»             negativos.append(x)
» except StopIteration:
»     print('São positivos:' , positivos, '\nSão negativos:' , negativos)

↳ São positivos: [1, 6, 0, 9, 76]
↳ São negativos: [-8, -3, -2, -45]

Embora a mesma funcionalidade possa ser conseguida de formas diferentes e mais compactas a transformação de um objeto iterável em um iterador é bastante útil para se escrever classes e métodos definidos pelo programador, como veremos.

Funções Lambda

Funções são boas formas de escrever código de fácil reutilização. No entanto, algumas vezes precisamos de definir um operador de forma compacta e que será utilizado apenas naquele ponto do código. As funções lambda (ou funções anônimas) permitem a criação de funções (que sequer recebem um nome) mas agem da forma desejada sobre seus argumentos.

Funções lambda tem uma sintaxe concisa:

lambda <arg1, ..., argn> : <expressão>

Elas só podem conter uma única expressão, nenhuma declaração, deve ser escrita em linha única e não comportam anotações. São exemplos simples de funções lambda:

# uma função bem simples é a identidade.
# a definição usual seria
» def id(x):
»     return x

# usando função lambda
» i = lambda x: x
» i(9)
↳ 9

# a função não precisa ser associada a uma variável
» (lambda x: abs(x))(-9)
↳ 9

# outros ex. Soma e multiplicação
» x = lambda a, b : a + b
» x(5,7)
↳ 12

» (lambda a, b : a * b)(5,9)
↳ 45

# múltiplos argumentos não são envoltos em parênteses na definição
» exp = lambda x,y : x**y
» exp(2,3)
↳ 8

» mix = lambda x,y,z : (x**y)/z
» mix(3,4,5)
↳ 16.2

# qualquer tipo de objeto pode ser passado como argumento
# no exemplo uma tupla é passada
» u = (4,7)
» (lambda x: x[0]*x[1])(u)
↳ 28

# uma função lamba que age sobre strings
# .title() torna o primeiro caracter maiúsculo
» nome_autor = lambda nome, sobrenome: f'Autor: {nome.title()} {sobrenome.title()}'
» nome_autor('richard','dawkins')
↳ 'Autor: Richard Dawkins'

A construção f'Texto {variavel}' permite a inserção de variáveis dentro de uma string.
Um exemplo de uso frequente consiste no uso de lambdas para fornecer um critério de ordenamento. Vimos que a função sorted(sequencia, key) pode ordenar uma sequência de acordo com um critério dado em key. No caso abaixo a cada tupla é associado o produto de seus pares, e a ordenação é feita nessa métrica.

» p = [(3, 3), (4, 2), (2, 2), (5, 2), (1, 7)] 
» sorted(p, key=lambda x: x[0]*x[1])
↳ [(2, 2), (1, 7), (4, 2), (3, 3), (5, 2)]

# dados vetores no espaço, ordená-los em ordem crescente de módulo
# a função lambda calcula o quadrado do módulo
» v =[(1,5,3), (1,8,2), (7,8,9), (2,4,3), (2,2,-1)]
» sorted(v, key=lambda x: x[0]**2+x[1]**2+x[2]**2)
↳ [(2, 2, -1), (2, 4, 3), (1, 5, 3), (1, 8, 2), (7, 8, 9)]

No exemplo seguinte definimos a função vezes_nque recebe o argumento n e opera sobre ele com a função lambda lambda a : a * n. Essa última, por sua vez, recebe outro argumento, no caso a. O exemplo também ilustra o fato de que uma função é um objeto e pode ser atribuída à uma variável.

# queremos uma função que multiplica seu argumento por um número especificado n
» # n pode ser visto como um parâmetro
» def vezes_n(n):
»     return lambda a : a * n

» funcao_duplicar = vezes_n(2)     # n = 2
» funcao_triplicar = vezes_n(3)    # n = 3
» funcao_x1000 = vezes_n(1000)     # n = 1000

» funcao_duplicar(23)              # n = 2, a = 23
↳ 46

» funcao_triplicar(15)             # n = 3, a = 15
↳ 45

» funcao_x1000(12.345)            # n = 1000, a = 12.345
↳ 12345.0

A minha_funcao abaixo é uma função lambda com dois argumentos sendo um deles outra função. No primeiro caso 2 + 2*10 = 22. No segundo caso 4 + (4)**3 -6 = 62.

» minha_funcao = lambda x, f: x + f(x)
» minha_funcao(2, lambda x: x*10 )     # aqui f(x) = x*10
↳ 22
» minha_funcao(4, lambda x: x**3-6)   # aqui f(x) = x**6 - 6
↳ 62

Função map()


Lambdas são também muito usadas junto com a função map() que tem a seguinte assinatura:

map(funcao, iteravel1, ..., iteraveln).

Pelo menos os argumentos funcao e um iterável são obrigatórios. A função percorre os iteráveis fornecidos usando a função especificada. Essa função deve usar um elemento de cada iterável. Ela usa um algoritmo otimizado para realizar sua transformação, o que a torna mais eficiente e rápida do que laços usuais do Python.

Ela retorna um objeto iterável map que pode ser convertido em lista com list(). Alguns exemplos:

# a operação abaixo concatena duas strings
» def concatena(a, b):
»     return (a + ' ' + b)
» iter1 = ('banana', 'laranja', 'uva')
» iter2 = ('nanica', 'bahia', 'syrah')
» x = map(concatena, iter1, iter2)

# para ver o resultado podemos percorrer a iterável gerada
# cada i no laço é uma única string (como 'banana nanica')
» for i in x:
»     print(i)
↳ banana nanica
↳ laranja bahia
↳ uva syrah

# a funcão pow(x,y) retorna x^y ou xy
# como pow age com 2 argumentos, duas sequências devem ser dadas em map
» y = map(pow, [6,7,8],[3,2,1])
» for i in y:
»     print(i, end=' - ')
↳ 216 - 49 - 8 -

# a função pode ser uma lambda
# duplicar cada elemento da lista
» lista = [10, 25, 17, 9, 30, -5]
» lista_2 = map(lambda n : n*2, lista)
# para exibir a sequência retornada podemos transformá-la em uma lista
» list(lista_2)
↳ [20, 50, 34, 18, 60, -10]

O uso de funções lambda pode tornar mais compacto o uso de map.

# para retornar uma lista de quadrados
» def quadrado(n):
»     return n**2
» numeros = [1, 2, 3, 4, 5]
» quadrados = map(quadrado, numeros)
» list(quadrados)
↳ [1, 4, 9, 16, 25]

# o mesmo resultado pode ser conseguido usando lambdas
» list(map(lambda x:x**2, range(1,6)))
↳ [1, 4, 9, 16, 25]

# abs() é função interna, que retorna valor absoluto
» nums = [-5, -3, -1, 0, 1, 3, 5]
» absolutos = list(map(abs, nums))
» absolutos
↳ [5, 3, 1, 0, 1, 3, 5]

# somar e multiplicar com 3 listas
» list(map(lambda i, j, k: i+j*k, [2, 4], [1, 3], [7, 8]))
↳ [9, 28]

# strip() remove string dados nas extremidades da string
» traco = ['---12356.876-', '-casa-da-vó---', '-mil-', '---']
» list(map(lambda s: s.strip("-"), traco))
↳ ['12356.876', 'casa-da-vó', 'mil', '']

# função recebe um número e retorna tuplas de 3 elementos
» def pot_1_2_3(x):
»     return x, x ** 2, x ** 3
» numeros = [1, 2, 3, 4]
» list(map(pot_1_2_3, numeros))
↳ [(1, 1, 1), (2, 4, 8), (3, 9, 27), (4, 16, 64)]

# a mesma coisa usando lambda
» list(map(lambda x: (x, x**2, x**3), numeros))
↳ [(1, 1, 1), (2, 4, 8), (3, 9, 27), (4, 16, 64)]

Em muitos casos uma compreensão de lista faz o mesmo que uma iteração via map(). No entanto é útil conhecer a função não apenas para poder ler e compreender código escrito por outras pessoas mas também para eventuais situações onde ela pode ser mais simples ou mais eficaz.

🔺Início do artigo


Arquivos e pastas

Bibliografia

Consulte a bibliografia no final do primeiro artigo dessa série.