Funções anônimas em Python

funcoes_lambda-1_abertura

As funções anônimas são rotinas definidas sem um identificador as associando¹ e são geralmente criadas para implementar pequenas funcionalidades ou para serem utilizadas por um período breve (dentro do escopo de uma função, por exemplo).

Em Python, as funções anônimas são definidas a partir da expressão lambda — por este motivo são também chamadas de “funções lambda” — e juntamente com os iteradores e os geradores são a base do paradigma da programação funcional nesta linguagem.

(¹) Sim, basicamente são funções que não tem nome mesmo! 🙂

Definindo uma função anônima

Uma função anônima, ou função lambda, é definida com a seguinte sintaxe.

lambda «argumentos» : «expressão»

Sendo argumentos correspondendo a uma ou mais variáveis utilizadas e expressão o código a ser executado — neste caso sem a necessidade do return para devolver o resultado.

Por exemplo, uma função anônima para calcular a média aritmética de uma lista de números poderia ser escrita como uma função anônima², desta forma.

media_1 = lambda valores : sum(valores) / len(valores)

Sendo equivalente à seguinte função.

>>> def media_2(valores):
...     return sum(valores) / len(valores)

E, em ambos os casos, utilizadas da mesma maneira.

>>> media_1([2, 3, 5, 6])
4
>>> media_2([2, 3, 5, 6])
4

(²) A função anônima não precisa estar necessariamente associada a uma variável, o exemplo foi apenas para ajudar a compará-las.

Sofisticando um pouco

As funções anônimas em Python mostram sua utilidade quando usadas em conjunto com algumas funções disponíveis como.

Função `filter()`

A função filter() permite iterar com cada um dos elementos de uma lista (ou outro elemento iterável), validando-os de acordo com o retorno da função e criando uma nova lista com o resultado — a função retornando verdadeiro para o valor, ele permanece e retornando falso, ele é filtrado.

Por exemplo, considerando esta lista com valores numéricos inteiros.

>>> numeros = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

É possível utilizar a função filter() para extrair apenas os números pares.

>>> list(filter(lambda i: i % 2 == 0, numeros))
[0, 2, 4, 6, 8]

O que equivaleria a uma função como esta.

def somente_pares(lista_de_numeros):
    lista_filtrada = []
    for i in lista_de_numeros:
        if i % 2 == 0:
            lista_filtrada.append(i)
    return lista_filtrada

Sim, sei que é possível fazer menor usando list comprehensions… 🙂

E para fazer o contrário, ou seja, filtrar apenas os números ímpares. Ao invés de definir uma nova função para a tarefa, basta uma pequena alteração na regra de validação.

>>> list(filter(lambda i: i % 2 != 0, numeros))
[1, 3, 5, 7, 9]

Em ambos os casos o uso da função list() aqui tem o objetivo de imprimir os valores ao invés da representação do objeto em si e é necessária somente para as versões 3.x do Python.

Função `map()`

O map() também itera com cada item da lista mas ao invés de efetuar uma validação, ele altera o valor do item com o resultado dela, criando uma nova lista com os respectivos resultados.

Por exemplo, para elevar todos os números da lista ao quadrado, faça.

>>> list(map(lambda i : i ** 2, numeros))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

O que é bastante útil para alterações em massa dos valores de uma lista. E ela equivalendo a uma função assim.

def ao_quadrado(lista_de_numeros):
    lista_ao_quadrado = []
    for i in lista_de_numeros:
        lista_ao_quadrado.append(i ** 2)
    return lista_ao_quadrado

Um outro exemplo, usando a função map() para converter a base numérica da lista de números de decimal para binário.

>>> list(map(lambda i : '{:04b}'.format(i), numeros))
['0000', '0001', '0010', '0011', '0100', '0101', '0110', '0111',
 '1000', '1001']

Neste caso a lista de números se transformou em uma lista de strings e só para lembrar que não há necessidade de manter o mesmo tipo nos dados.

Função `reduce()`

Por último, a função reduce() que reduz a quantidade de itens de uma lista iterando seus elementos em pares até que reste apenas um valor. Para o caso das versões 3.x do Python, é preciso carregá-la do módulo functools, no caso da versão 2.7.x isto não é necessário, ela está embutida na linguagem.

>>> from functools import reduce

Um exemplo simples com o uso da função reduce() para obter a soma de todos os valores da lista. Atenção para os parênteses envolvendo o lambda, eles servem para evitar que a vírgula seja confundida com separador de parâmetros da função reduce().

>>> reduce((lambda i, j : i + j), numeros)
45

E um modo mais visual, para entender de onde surgem os valores para ‘i’ e ‘j’ durante a execução da função reduce() é a seguinte:

 i + j
 0 + 1 2 3 4 5 6 7 8 9
 1 + 2 3 4 5 6 7 8 9
 3 + 3 4 5 6 7 8 9
 6 + 4 5 6 7 8 9
10 + 5 6 7 8 9
15 + 6 7 8 9
21 + 7 8 9
28 + 8 9
36 + 9 
45

Claro, o mesmo resultado poderia ser obtido com o uso da função sum() mas e se o objetivo fosse totalizar apenas os itens com valores menores que 8?

>>> reduce((lambda i,j : i+j if j < 8 else i), numeros)
28

Ou totalizar apenas valores com números pares?

>>> reduce((lambda i,j : i+j if j % 2 == 0 else i), numeros)
20

Ou seja, é possível customizar a redução usando diversos critérios de seleção e também de operação ente os itens — aliás, optei pela adição para manter estes exemplos simples.

Considerações finais

Junto com os iteradores e geradores as funções anônimas servem como recursos para a implementação do paradigma da programação funcional em Python. Além daquelas já citadas a linguagem implementa diversas outras funções bastante interessantes para para a manipulação dos valores embutidos na própria linguagem ou dentro de módulos como o functools ou mesmo o itertools.

E para terminar, lembrar que as funções filter(), map() e reduce() também podem ser utilizadas para chamar funções convencionais, não necessariamente anônimas.

Anúncios