Mais preferível: funções lambda ou funções aninhadas ( def
)?
Há uma vantagem em usar um lambda em vez de uma função regular: eles são criados em uma expressão.
Existem várias desvantagens:
- sem nome (apenas
'<lambda>'
)
- sem docstrings
- sem anotações
- sem declarações complexas
Eles também são o mesmo tipo de objeto. Por esses motivos, geralmente prefiro criar funções com a def
palavra - chave em vez de lambdas.
Primeiro ponto - eles são o mesmo tipo de objeto
Um lambda resulta no mesmo tipo de objeto que uma função regular
>>> l = lambda: 0
>>> type(l)
<class 'function'>
>>> def foo(): return 0
...
>>> type(foo)
<class 'function'>
>>> type(foo) is type(l)
True
Como lambdas são funções, eles são objetos de primeira classe.
Lambdas e funções:
- pode ser transmitido como um argumento (o mesmo que uma função regular)
- quando criado dentro de uma função externa, torna-se um fechamento sobre os locais das funções externas
Mas lambdas estão, por padrão, perdendo algumas coisas que as funções obtêm por meio da sintaxe de definição de função completa.
Um lamba __name__
é'<lambda>'
Afinal, lambdas são funções anônimas, então eles não sabem o próprio nome.
>>> l.__name__
'<lambda>'
>>> foo.__name__
'foo'
Portanto, lambda's não podem ser pesquisados programaticamente em seu namespace.
Isso limita certas coisas. Por exemplo, foo
pode ser pesquisado com código serializado, enquanto l
não pode:
>>> import pickle
>>> pickle.loads(pickle.dumps(l))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
_pickle.PicklingError: Can't pickle <function <lambda> at 0x7fbbc0464e18>:
attribute lookup <lambda> on __main__ failed
Podemos pesquisar foo
muito bem - porque ele conhece seu próprio nome:
>>> pickle.loads(pickle.dumps(foo))
<function foo at 0x7fbbbee79268>
Lambdas não têm anotações nem docstring
Basicamente, lambdas não são documentados. Vamos reescrever foo
para ser melhor documentado:
def foo() -> int:
"""a nullary function, returns 0 every time"""
return 0
Agora, foo tem documentação:
>>> foo.__annotations__
{'return': <class 'int'>}
>>> help(foo)
Help on function foo in module __main__:
foo() -> int
a nullary function, returns 0 every time
Considerando que, não temos o mesmo mecanismo para fornecer as mesmas informações para lambdas:
>>> help(l)
Help on function <lambda> in module __main__:
<lambda> lambda (...)
Mas podemos hackea-los:
>>> l.__doc__ = 'nullary -> 0'
>>> l.__annotations__ = {'return': int}
>>> help(l)
Help on function <lambda> in module __main__:
<lambda> lambda ) -> in
nullary -> 0
Porém, provavelmente há algum erro atrapalhando a saída da ajuda.
Lambdas só pode retornar uma expressão
Lambdas não podem retornar declarações complexas, apenas expressões.
>>> lambda: if True: 0
File "<stdin>", line 1
lambda: if True: 0
^
SyntaxError: invalid syntax
As expressões podem ser bastante complexas e, se você tentar muito, provavelmente conseguirá o mesmo com um lambda, mas a complexidade adicionada é mais prejudicial à escrita de código claro.
Usamos Python para maior clareza e facilidade de manutenção. O uso excessivo de lambdas pode funcionar contra isso.
A única vantagem para lambdas: pode ser criado em uma única expressão
Esta é a única vantagem possível. Já que você pode criar um lambda com uma expressão, você pode criá-lo dentro de uma chamada de função.
Criar uma função dentro de uma chamada de função evita a pesquisa de nome (barata) em comparação com uma criada em outro lugar.
No entanto, como o Python é avaliado estritamente, não há outro ganho de desempenho em fazer isso além de evitar a consulta de nome.
Para uma expressão muito simples, posso escolher um lambda.
Eu também tendo a usar lambdas ao fazer Python interativo, para evitar várias linhas quando uma delas faz. Eu uso o seguinte tipo de formato de código quando quero passar um argumento para um construtor ao chamar timeit.repeat
:
import timeit
def return_nullary_lambda(return_value=0):
return lambda: return_value
def return_nullary_function(return_value=0):
def nullary_fn():
return return_value
return nullary_fn
E agora:
>>> min(timeit.repeat(lambda: return_nullary_lambda(1)))
0.24312214995734394
>>> min(timeit.repeat(lambda: return_nullary_function(1)))
0.24894469301216304
Acredito que a ligeira diferença de tempo acima pode ser atribuída à pesquisa de nome em return_nullary_function
- observe que é muito insignificante.
Conclusão
Lambdas são bons para situações informais em que você deseja minimizar linhas de código em favor de fazer um ponto singular.
Lambdas são ruins para situações mais formais em que você precisa de clareza para editores de código que virão mais tarde, especialmente nos casos em que não são triviais.
Sabemos que devemos dar bons nomes aos nossos objetos. Como podemos fazer isso quando o objeto não tem nome?
Por todos esses motivos, geralmente prefiro criar funções com em def
vez de com lambda
.
lambda
, mas discordo que isso é "muito raro", é comum para funções-chave parasorted
ouitertools.groupby
etc., por exemplosorted(['a1', 'b0'], key= lambda x: int(x[1]))