Desde a versão 1.0 do Python, as funções lambda são uti­li­za­das como meio para a pro­gra­ma­ção funcional. Atu­al­mente, a utilidade desta função foi am­pla­mente subs­ti­tuída por outras técnicas. No entanto, ainda existem algumas áreas de uso especial que os pro­gra­ma­do­res ex­pe­ri­en­tes em Python devem conhecer.

O que são funções lambda em Python?

O termo “função lambda” significa função anónima em Python. Para criar uma função lambda, o Python utiliza a palavra-chave lambda. Uma expressão lambda consiste na palavra-chave lambda seguida de uma lista de ar­gu­men­tos, dois pontos e uma única expressão (“ex­pres­sion”). Assim que a função lambda é chamada, a expressão é fornecida com os ar­gu­men­tos e avaliada:

lambda argument: expression

As funções são uma cons­tru­ção lin­guís­tica fun­da­men­tal de quase todas as lin­gua­gens de pro­gra­ma­ção e re­pre­sen­tam a menor unidade de código reu­ti­li­zá­vel. Nor­mal­mente, as funções em Python são definidas com a palavra-chave def. Por exemplo, este também seria o caso da função square, que mul­ti­plica um número por si mesmo:

# Define square function
def square(num):
    return num * num
# Show that it works
assert square(9) == 81
python

Além da forma mais conhecida de definir funções em Python usando a palavra-chave def, a linguagem reconhece as “lambdas”. Estas são funções curtas e anónimas (ou seja, sem nome) que definem uma expressão com pa­râ­me­tros. Pode usar lambdas em qualquer lugar onde uma função seja esperada ou pode vinculá-las a um nome por meio de uma atri­bui­ção. Aqui está a expressão lambda equi­va­lente à função square:

# Create square function
squared = lambda num: num * num
# Show that it works
assert squared(9) == 81
python

Qual é a diferença entre lambda e def?

À primeira vista, parece estranho que o Python reconheça duas formas de criar funções: lambda e def. Mas é preciso ter em conta que lambda não é uma função pro­pri­a­mente dita, mas sim­ples­mente uma notação diferente para criar funções curtas lo­cal­mente. Todas as funções criadas com lambda também podem ser criadas com def. No entanto, o inverso não é ver­da­deiro.

A nível sintático, tanto lambda como def são palavras-chave. Uma diferença entre ambas reside na separação estrita que o Python faz entre sentenças («Statement») e ex­pres­sões («Ex­pres­sion»). As sentenças são passos na execução do código, enquanto as ex­pres­sões são avaliadas em relação a um valor.

Com def começa uma instrução (con­cre­ta­mente uma «Compound Statement») que contém mais ins­tru­ções. Dentro da instrução def, e apenas aí, podem aparecer as ins­tru­ções return. Após a chamada à função definida com def, uma instrução return devolve um valor.

Ao contrário da sentença def, lambda inicia uma expressão que não deve conter nenhuma sentença. A expressão lambda recebe um ou mais ar­gu­men­tos e retorna uma função anónima. Se a função lambda gerada for chamada, será feita uma avaliação da expressão contida com os ar­gu­men­tos passados e o resultado será retornado.

Quais são as li­mi­ta­ções das ex­pres­sões lambda do Python?

O Python limita es­pe­ci­fi­ca­mente a utilidade das funções lambda, pois ge­ral­mente é melhor nomear as funções. Isso obriga os pro­gra­ma­do­res a pensar no sig­ni­fi­cado da função e a dis­tin­guir cla­ra­mente algumas partes das outras.

Ao contrário do corpo de uma função definida pela palavra-chave def, as lambdas não podem conter ins­tru­ções. Portanto, não é possível utilizar if, for, etc. dentro de uma função lambda. Também não é possível lançar uma exceção (Exception), pois para isso é ne­ces­sá­ria a instrução raise.

As funções lambda em Python só podem conter uma única expressão que é avaliada após ser chamada. Dentro da expressão lambda, não é possível utilizar anotações de tipo. Atu­al­mente, outras técnicas são uti­li­za­das para a maioria dos casos de uso das funções lambda do Python. Es­pe­ci­al­mente nas com­pre­en­sões.

Para que servem as funções lambda do Python?

As lambdas surgem da pro­gra­ma­ção funcional. Em algumas lin­gua­gens, como Ja­vaS­cript, fre­quen­te­mente são uti­li­za­das funções anónimas sem uma palavra-chave especial. Em Python, as ex­pres­sões lambda são uti­li­za­das para criar pequenas funções lo­cal­mente, sem grandes com­pli­ca­ções. Mostramos-lhe os casos de uti­li­za­ção mais úteis.

Equipar funções de ordem superior com lambdas em Python

As lambdas são fre­quen­te­mente uti­li­za­das em conexão com funções de ordem superior, como map(), filter() e reduce(). Com a sua ajuda, é possível trans­for­mar os elementos de um «iterável» sem utilizar loops. As funções de ordem superior são funções que recebem funções como pa­râ­me­tros ou retornam uma função.

A função map() recebe como pa­râ­me­tros uma função e um iterável e executa a função para cada elemento do iterável. Considere o problema de gerar números quadrados: usamos a função map() e passamos como argumento uma expressão lambda, que gera a função quadrado. Com map(), a função quadrado é aplicada a cada elemento da lista:

nums = [3, 5, 7]
# Square numbers using using `map()` and `lambda`
squares = map(lambda num: num ** 2, nums)
# Show that it works
assert list(squares) == [9, 25, 49]
python
Nota

A partir do Python 3.0, as funções map() e filter() retornam um iterável em vez de uma lista. Pode usar list() dentro da instrução assert para des­com­pac­tar o iterável numa lista.

Com as com­pre­en­sões de listas, existe agora uma abordagem moderna e preferida para processar iteráveis. Em vez de recorrer ao map() e criar uma função lambda, a operação pode ser descrita di­re­ta­mente:

nums = [3, 5, 7]
# Square numbers using list comprehension
squares = [num ** 2 for num in nums]
# Show that it works
assert squares == [9, 25, 49]
python

Com a função filter(), é possível filtrar os elementos de um iterável. Ampliamos o nosso exemplo para que apenas sejam gerados números quadrados pares:

# List of numbers 1–4
nums = [1, 2, 3, 4]
# Square each number
squares = list(map(lambda num: num ** 2, nums))
# Filter out the even squares
even_squares = filter(lambda square: square % 2 == 0, squares)
# Show that it works
assert list(even_squares) == [4, 16]
python

Voltamos a mostrar a abordagem moderna e preferida de usar a com­pre­en­são de listas para gerar o mesmo resultado sem usar lambdas ou funções de ordem superior. Usamos a parte if da com­pre­en­são para filtrar os números pares dos números quadrados:

# List of numbers 1–4 squared
squares = [num ** 2 for num in range(1, 5)]
# Filter out the even squares
even_squares = [square for square in squares if square % 2 == 0]
# Show that it works
assert even_squares == [4, 16]
python
Nota

A função reduce() do Python já não faz parte da bi­bli­o­teca padrão desde o Python 3.0. Foi trans­fe­rida para o módulo functools.

Executar funções-chave com lambdas em Python

As com­pre­en­sões subs­ti­tuí­ram em grande parte o uso das funções clássicas de ordem superior map() e filter() em Python. No entanto, com as “Key-Functions”, existe um cenário de uso em que as lambdas mostram ple­na­mente os seus pontos fortes.

As funções de com­pa­ra­ção do Python sorted(), min() e max() operam sobre iteráveis. Quando esta função é chamada, cada elemento do iterável é comparado. As três funções aceitam uma função chave como parâmetro opcional key. A função chave trata cada elemento e retorna um valor chave para a operação de com­pa­ra­ção.

Como exemplo, considere o seguinte problema. Imagine que tem uma pasta com ficheiros de imagem cujos nomes estão numa lista Python. Quer ordenar a lista. Todos os nomes dos ficheiros começam por img, seguido de uma numeração:

# List of image file names
images = ['img1', 'img2', 'img30', 'img3', 'img22', 'img100']
python

Se utilizar a função sorted() do Python, é utilizada a «ordem le­xi­co­grá­fica». Isto significa que os dígitos con­se­cu­ti­vos são tratados como números únicos. Desta forma, os números ['1', '2', '100'] são colocados na ordem ['1', '100', '2']. Ou seja, o resultado não cor­res­ponde às ex­pec­ta­ti­vas:

# Sort using lexicographic order
sorted_image = sorted(images)
# Show that it works
assert sorted_image == ['img1', 'img100', 'img2', 'img22', 'img3', 'img30']
python

Para que a ordem se adapte às suas ne­ces­si­da­des, usamos uma expressão lambda que cria uma função-chave. Isso permite extrair a parte numérica de um nome de ficheiro para que sorted() a utilize como chave:

# Extract numeric component and sort as integers
sorted_image = sorted(images, key=lambda name: int(name[3:]))
# Show that it works
assert sorted_image == ['img1', 'img2', 'img3', 'img22', 'img30', 'img100']
python

A função chave é utilizada apenas lo­cal­mente e uma única vez. Não é ne­ces­sá­rio definir uma função com nome adicional para ela. Por isso, as lambdas são o meio adequado para criar funções chave. Vejamos outros dois exemplos.

Além de sorted(), as funções in­cor­po­ra­das do Python min() e max() aceitam uma função-chave opcional. As funções encontram o menor ou o maior elemento de uma lista ou outro iterável. O que constitui exa­ta­mente o menor ou o maior elemento é uma questão de definição e pode ser es­pe­ci­fi­cado pela função-chave.

Com listas de valores simples, por exemplo, uma lista de números, fica claro o que se entende por elemento «menor» ou «maior». Aqui não é ne­ces­sá­ria uma função-chave especial:

nums = [42, 69, 51, 13]
assert min(nums) == 13
assert max(nums) == 69
python
Nota

Se nenhuma função chave for passada, a função de iden­ti­dade f(x) = x é usada im­pli­ci­ta­mente. Ela pode ser fa­cil­mente definida como uma lambda Python com lambda x: x.

Mas o que acontece se os elementos de um iterável com­pre­en­de­rem várias datas cada um? Imagine uma lista de di­ci­o­ná­rios («dicts») que re­pre­sen­tam pessoas com nome e idade. De acordo com que critério min() e max() devem decidir qual elemento é o menor ou o maior? Para isso, utiliza-se exa­ta­mente a função chave.

Para ilustrar como funcionam as funções prin­ci­pais, pre­ci­sa­mos de dados de exemplo. Portanto, crie uma função Person() que sirva como cons­tru­tor:

# Constructor function for dict representing a person
def Person(name, age):
    return {'name': name, 'age': age}
# Check that it works as expected
assert Person('Jim', 42) == {'name': 'Jim', 'age': 42}
python

Com a ajuda da função cons­tru­tora, crie uma lista de pessoas:

# Create list of people
people = [Person('Jim', 42), Person('Jack', 51), Person('John', 69)]
python

Em seguida, procure a pessoa mais velha usando a chamada max(). Depois de realizar esta etapa, é gerada uma função-chave por meio de uma expressão lambda que pega um di­ci­o­ná­rio de pessoas e extrai dele a idade como elemento de com­pa­ra­ção:

# Find the oldest person
oldest = max(people, key=lambda person: person['age'])
# Check that it works
assert oldest == Person('John', 69)
python

A abordagem é exa­ta­mente a mesma para a função min(). Aqui, é ne­ces­sá­rio definir a função chave fora da chamada min(), uti­li­zando, novamente, uma expressão lambda. Isso melhora a le­gi­bi­li­dade e vale a pena se a função chave tiver vários usos locais:

# Define key function to compare people by age
by_age = lambda person: person['age']
# Find the youngest person
youngest = min(people, key=by_age)
# Check that it works
assert youngest == Person('Jim', 42)
python

Criação de closures com lambdas em Python

Outra uti­li­za­ção das lambdas do Python é a definição das «closures», funções que são criadas por outras funções e armazenam um valor. As closures podem ser uti­li­za­das, por exemplo, para criar famílias de funções se­me­lhan­tes. A seguir, mostramos o exemplo habitual: criar funções de potência.

As funções de potência recebem um argumento e o elevam à potência. Exemplos co­nhe­ci­dos são a função quadrada f(x) = x ^ 2 e a função cúbica f(x) = x ^ 3. Por meio de uma função cons­tru­tora, é possível gerar qualquer função de potência como closure. Utilize uma expressão lambda e poupe-se assim de definir uma função interna com nome:

# Define constructor function for power functions
def power(n):
    return lambda num: num ** n
# Create square and cubic functions as closures
square = power(2)
cubic = power(3)
# Show that it works
assert square(10) == 100
assert cubic(10) == 1000
python

Expressão de função invocada ime­di­a­ta­mente (IIFE) com lambdas em Python

IIFE, pro­nun­ci­ado «iffy», é um padrão conhecido em Ja­vaS­cript. Define uma função anónima e executa-a ime­di­a­ta­mente.

Embora não sejam muito úteis em Python devido à sua restrição de uso, as lambdas podem ser uti­li­za­das como IIFE. Basta colocar pa­rên­te­ses em torno da expressão lambda:

(lambda num: num * num)
python

Além disso, outro par de pa­rên­te­ses que contenham o argumento ou ar­gu­men­tos:

assert (lambda num: num * num)(3) == 9
python
Ir para o menu principal