Funções lambda do Python: funções anónimas no Python
Desde a versão 1.0 do Python, as funções lambda são utilizadas como meio para a programação funcional. Atualmente, a utilidade desta função foi amplamente substituída por outras técnicas. No entanto, ainda existem algumas áreas de uso especial que os programadores experientes 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 argumentos, dois pontos e uma única expressão (“expression”). Assim que a função lambda é chamada, a expressão é fornecida com os argumentos e avaliada:
lambda argument: expressionAs funções são uma construção linguística fundamental de quase todas as linguagens de programação e representam a menor unidade de código reutilizável. Normalmente, 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 multiplica um número por si mesmo:
# Define square function
def square(num):
return num * num
# Show that it works
assert square(9) == 81pythonAlé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 parâmetros. Pode usar lambdas em qualquer lugar onde uma função seja esperada ou pode vinculá-las a um nome por meio de uma atribuição. Aqui está a expressão lambda equivalente à função square:
# Create square function
squared = lambda num: num * num
# Show that it works
assert squared(9) == 81pythonQual é 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 propriamente dita, mas simplesmente uma notação diferente para criar funções curtas localmente. Todas as funções criadas com lambda também podem ser criadas com def. No entanto, o inverso não é verdadeiro.
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 expressões («Expression»). As sentenças são passos na execução do código, enquanto as expressões são avaliadas em relação a um valor.
Com def começa uma instrução (concretamente uma «Compound Statement») que contém mais instruções. Dentro da instrução def, e apenas aí, podem aparecer as instruçõ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 argumentos 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 argumentos passados e o resultado será retornado.
Quais são as limitações das expressões lambda do Python?
O Python limita especificamente a utilidade das funções lambda, pois geralmente é melhor nomear as funções. Isso obriga os programadores a pensar no significado da função e a distinguir claramente algumas partes das outras.
Ao contrário do corpo de uma função definida pela palavra-chave def, as lambdas não podem conter instruçõ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 é necessá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. Atualmente, outras técnicas são utilizadas para a maioria dos casos de uso das funções lambda do Python. Especialmente nas compreensões.
Para que servem as funções lambda do Python?
As lambdas surgem da programação funcional. Em algumas linguagens, como JavaScript, frequentemente são utilizadas funções anónimas sem uma palavra-chave especial. Em Python, as expressões lambda são utilizadas para criar pequenas funções localmente, sem grandes complicações. Mostramos-lhe os casos de utilização mais úteis.
Equipar funções de ordem superior com lambdas em Python
As lambdas são frequentemente utilizadas em conexão com funções de ordem superior, como map(), filter() e reduce(). Com a sua ajuda, é possível transformar os elementos de um «iterável» sem utilizar loops. As funções de ordem superior são funções que recebem funções como parâmetros ou retornam uma função.
A função map() recebe como parâmetros 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]pythonA 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 descompactar o iterável numa lista.
Com as compreensõ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 diretamente:
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]pythonCom 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]pythonVoltamos a mostrar a abordagem moderna e preferida de usar a compreensão de listas para gerar o mesmo resultado sem usar lambdas ou funções de ordem superior. Usamos a parte if da compreensã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]pythonA função reduce() do Python já não faz parte da biblioteca padrão desde o Python 3.0. Foi transferida para o módulo functools.
Executar funções-chave com lambdas em Python
As compreensões substituí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 plenamente os seus pontos fortes.
As funções de comparaçã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 comparaçã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']pythonSe utilizar a função sorted() do Python, é utilizada a «ordem lexicográfica». Isto significa que os dígitos consecutivos 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 corresponde às expectativas:
# Sort using lexicographic order
sorted_image = sorted(images)
# Show that it works
assert sorted_image == ['img1', 'img100', 'img2', 'img22', 'img3', 'img30']pythonPara que a ordem se adapte às suas necessidades, 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']pythonA função chave é utilizada apenas localmente e uma única vez. Não é necessá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 incorporadas 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 exatamente o menor ou o maior elemento é uma questão de definição e pode ser especificado 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 é necessária uma função-chave especial:
nums = [42, 69, 51, 13]
assert min(nums) == 13
assert max(nums) == 69pythonSe nenhuma função chave for passada, a função de identidade f(x) = x é usada implicitamente. Ela pode ser facilmente definida como uma lambda Python com lambda x: x.
Mas o que acontece se os elementos de um iterável compreenderem várias datas cada um? Imagine uma lista de dicionários («dicts») que representam 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 exatamente a função chave.
Para ilustrar como funcionam as funções principais, precisamos de dados de exemplo. Portanto, crie uma função Person() que sirva como construtor:
# 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}pythonCom a ajuda da função construtora, crie uma lista de pessoas:
# Create list of people
people = [Person('Jim', 42), Person('Jack', 51), Person('John', 69)]pythonEm 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 dicionário de pessoas e extrai dele a idade como elemento de comparação:
# Find the oldest person
oldest = max(people, key=lambda person: person['age'])
# Check that it works
assert oldest == Person('John', 69)pythonA abordagem é exatamente a mesma para a função min(). Aqui, é necessário definir a função chave fora da chamada min(), utilizando, novamente, uma expressão lambda. Isso melhora a legibilidade 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)pythonCriação de closures com lambdas em Python
Outra utilizaçã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 utilizadas, por exemplo, para criar famílias de funções semelhantes. 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 conhecidos 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 construtora, é 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) == 1000pythonExpressão de função invocada imediatamente (IIFE) com lambdas em Python
IIFE, pronunciado «iffy», é um padrão conhecido em JavaScript. Define uma função anónima e executa-a imediatamente.
Embora não sejam muito úteis em Python devido à sua restrição de uso, as lambdas podem ser utilizadas como IIFE. Basta colocar parênteses em torno da expressão lambda:
(lambda num: num * num)pythonAlém disso, outro par de parênteses que contenham o argumento ou argumentos:
assert (lambda num: num * num)(3) == 9python