O que é mais preferível usar em Python: funções lambda ou funções aninhadas ( 'def')?

StackOverflow https://stackoverflow.com/questions/134626

Pergunta

Eu use funções lambda, mas às vezes usar funções aninhadas que parecem oferecer o mesmo comportamento.

Aqui estão alguns exemplos triviais onde eles funcionalmente fazer a mesma coisa se quer foram encontrados dentro de outra função:

Função Lambda

>>> a = lambda x : 1 + x
>>> a(5)
6

Função Nested

>>> def b(x): return 1 + x

>>> b(5)
6

Existem vantagens em usar um sobre o outro? (Performance? Legibilidade? Limitações? Consistência? Etc.)

Será que importa mesmo? Se não, em seguida, faz isso viola o princípio Pythonic:

“Deve haver um e de preferência apenas um óbvio maneira de fazê-lo” .

Foi útil?

Solução

Se você precisar atribuir o lambda a um nome, use uma def vez. defs são apenas açúcar sintático para uma tarefa, de modo que o resultado é o mesmo, e eles são muito mais flexível e de fácil leitura.

lambdas pode ser usado para usar uma vez, jogue fora funções que não terá um nome.

No entanto, este caso de uso é muito raro. Você raramente precisa passar em torno de objetos de função sem nome.

Os builtins map() e objetos de função necessidade filter(), mas compreensões lista e gerador de expressões são geralmente mais legível do que essas funções e pode cobrir todos os casos de uso, sem a necessidade de lambdas.

Para os casos que você realmente precisa de um pequeno objeto de função, você deve usar as funções do módulo operator, como operator.add vez de lambda x, y: x + y

Se você ainda precisa de algum lambda não cobertas, você pode considerar escrever um def, apenas para ser mais legível. Se a função é mais complexa do que os de módulo operator, um def é provavelmente melhor.

Assim, no mundo real os casos de uso bom lambda são muito raros.

Outras dicas

Em termos práticos, para mim há duas diferenças:

A primeira é sobre o que eles fazem eo que eles retornam:

  • def é uma palavra-chave que não retorna nada e cria um 'nome' no namespace local.

  • lambda é uma palavra-chave que retorna um objeto de função e não cria um 'nome' no namespace local.

Assim, se você precisa chamar uma função que recebe um objeto de função, a única maneira de fazer isso em uma linha de código python é com um lambda. Não há equivalente com def.

Em alguns enquadramentos isso é realmente muito comum; por exemplo, eu uso torcida muito, e assim fazendo algo como

d.addCallback(lambda result: setattr(self, _someVariable, result))

é bastante comum, e mais conciso com lambdas.

A segunda diferença é sobre o que a função real é permitido fazer.

  • Uma função definida com 'def' pode conter qualquer código Python
  • Uma função definida com 'lambda' tem que avaliar a uma expressão, e, portanto, não pode conter declarações como impressão, importação, aumento, ...

Por exemplo,

def p(x): print x

funciona como esperado, enquanto

lambda x: print x

é um SyntaxError.

É claro, existem soluções alternativas - print substituto com sys.stdout.write, ou import com __import__. Mas geralmente é melhor ir com uma função nesse caso.

Nesta entrevista, Guido van Rossum diz ele deseja que ele não tinha deixado 'lambda 'em Python:

" Q. O que têm de Python que você está menos satisfeito com?
Às vezes eu tenho sido muito rápido em aceitar contribuições, e mais tarde percebi que era um erro. Um exemplo seria alguns dos recursos de programação funcional, tais como funções lambda. lambda é uma palavra-chave que lhe permite criar uma pequena função anônima; incorporado em funções tais como o mapa, filtrar, e reduzir a executar uma função ao longo de um tipo de sequência, tal como uma lista.
Na prática, não saíram tão bem. Python tem apenas dois âmbitos: local e global. Isso faz com que escrever funções lambda doloroso, porque você quer muitas vezes para as variáveis ??de acesso no âmbito onde o lambda foi definida, mas você não pode por causa dos dois âmbitos. Há uma maneira de contornar isso, mas é uma espécie de truque. Muitas vezes parece muito mais fácil em Python para usar apenas um loop em vez de andar com funções lambda. mapa e amigos funcionam bem apenas quando já há um built-in função que faz o que quiser.

IMHO, Iambdas pode ser conveniente às vezes, mas geralmente são convenientes à custa da legibilidade. Você pode me dizer o que isso faz:

str(reduce(lambda x,y:x+y,map(lambda x:x**x,range(1,1001))))[-10:]

Eu escrevi isso, e ele me levou um minuto para descobrir isso. Isto é de Projeto Euler - Não vou dizer que problema porque eu odeio spoilers, mas ele é executado em 0,124 segundos:)

Para n = 1000 aqui estão algumas timeit de de chamar uma função vs um lambda:

In [11]: def f(a, b):
             return a * b

In [12]: g = lambda x, y: x * y

In [13]: %%timeit -n 100
for a in xrange(n):
  for b in xrange(n):
    f(a, b)
   ....:
100 loops, best of 3: 285 ms per loop

In [14]: %%timeit -n 100
for a in xrange(n):
  for b in xrange(n):
    g(a, b)
   ....:
100 loops, best of 3: 298 ms per loop

In [15]: %%timeit -n 100
for a in xrange(n):
  for b in xrange(n):
    (lambda x, y: x * y)(a, b)
   ....:
100 loops, best of 3: 462 ms per loop

Estou de acordo com o conselho de nosklo: se você precisa dar a função de um nome, o uso def. I reservar funções lambda para casos onde eu estou apenas passando um breve trecho de código para outra função, por exemplo:.

a = [ (1,2), (3,4), (5,6) ]
b = map( lambda x: x[0]+x[1], a )

Performance:

Criação de uma função com lambda é ligeiramente mais rápido do que criá-la com def. A diferença é devido a def criar uma entrada de nome na tabela de habitantes. A função resultante tem a mesma velocidade de execução.


Legibilidade:

funções lambda são um pouco menos legível para a maioria dos usuários de Python, mas também muito mais conciso em algumas circunstâncias. Considerar a conversão do uso de não-funcional à rotina funcional:

# Using non-functional version.

heading(math.sqrt(v.x * v.x + v.y * v.y), math.atan(v.y / v.x))

# Using lambda with functional version.

fheading(v, lambda v: math.sqrt(v.x * v.x + v.y * v.y), lambda v: math.atan(v.y / v.x))

# Using def with functional version.

def size(v):
    return math.sqrt(v.x * v.x + v.y * v.y)

def direction(v):
    return math.atan(v.y / v.x)

deal_with_headings(v, size, direction)

Como você pode ver, a versão lambda é mais curto e "mais fácil" no sentido de que você só precisa adicionar lambda v: à versão original não-funcional para converter para a versão funcional. É também muito mais concisa. Mas lembre-se, um monte de usuários Python vai ser confundido com a sintaxe lambda, de modo que você perde em extensão e complexidade reais pode ser adquirida de volta em confusão de colegas programadores.


Limitações:

  • funções lambda só pode ser usado uma vez, a não ser atribuído a um nome de variável.
  • funções lambda atribuídos a nomes de variáveis ??não têm nenhuma vantagem sobre funções def.
  • funções lambda pode ser difícil ou impossível de picles.
  • nomes de funções def deve ser cuidadosamente escolhido para ser razoavelmente descritivo e único ou pelo menos não usada no espaço.

Consistência:

Python evita principalmente convenções de programação funcional em favor de semântica objetivas processuais e mais simples. O operador lambda está em contraste directo com este viés. Além disso, como uma alternativa para o def já prevalente, a função lambda acrescenta diversidade à sua sintaxe. Alguns considerariam que menos consistente.


funções pré-existentes:

Como conhecida por outros, muitos usos de lambda no campo pode ser substituído por membros da operator ou outros módulos. Por exemplo:

do_something(x, y, lambda x, y: x + y)
do_something(x, y, operator.add)

Usando a função de pré-existente pode tornar o código mais legível em muitos casos.


O princípio Pythonic: “Deve haver um e de preferência apenas um óbvio maneira de fazê-lo”

Isso é semelhante ao única fonte de doutrina verdade . Infelizmente, o caminho single-óbvia-to-do-it princípio sempre foi mais uma aspiração ansiosa para Python, em vez de um verdadeiro orientador diretor. Considere os muito poderosos compreensões matriz em Python. Eles são funcionalmente equivalentes às funções map e filter:

[e for e in some_array if some_condition(e)]
filter(some_array, some_condition)

lambda e def são os mesmos.

É uma questão de opinião, mas eu diria que qualquer coisa na linguagem Python para uso geral que, obviamente, não quebrar nada é "Pythonic" o suficiente.

Embora concordando com as outras respostas, às vezes é mais legível. Aqui está um exemplo onde lambda vem a calhar, em um caso de uso Continuo encontrando de uma N dimensional defaultdict
Aqui está um exemplo:

from collections import defaultdict
d = defaultdict(lambda: defaultdict(list))
d['Foo']['Bar'].append(something)

Acho que é mais legível do que a criação de um def para a segunda dimensão. Isto é ainda mais significativo para dimensões superiores.

O uso primário de lambda tem sido sempre para funções de retorno simples, e por mapa, reduzir, filtro, que exigem uma função como um argumento. Com compreensões lista se tornando a norma, eo adicionado permitido se como em:

x = [f for f in range(1, 40) if f % 2]

é difícil imaginar um caso real para o uso de lambda no uso diário. Como resultado, eu diria, evite lambda e criar funções aninhadas.

Uma importante limitação do lambdas é que eles não podem conter nada além de uma expressão. É quase impossível para uma expressão lambda para produzir nada além de efeitos colaterais triviais, uma vez que não se pode ter em qualquer lugar perto como o corpo de uma rica como uma função def'ed.

Dito isto, Lua influenciado meu estilo de programação para o uso extensivo de funções anônimas, e eu lixo meu código com eles. Em cima disso, eu tendem a pensar sobre mapa / reduzir, operadores abstratos de maneiras que eu não consideram compreensões lista ou geradores, quase como se eu estou adiando uma decisão de execução explicitamente usando os operadores.

Editar:. Esta é uma questão bastante antiga, e as minhas opiniões sobre o assunto mudaram, tanto

Primeiro, eu estou fortemente inclinado contra a atribuição de uma expressão lambda a uma variável; como python tem uma sintaxe especial só para isso (dica, def). Além disso, muitos dos usos para lambda, mesmo quando eles não recebem um nome, têm predefinidos (e mais eficiente) implementações. Por exemplo, o exemplo em questão pode ser abreviado para apenas (1).__add__, sem a necessidade de envolvê-la em um lambda ou def. Muitos outros usos comuns pode ser satisfeita com alguma combinação dos módulos operator, itertools e functools.

Mais preferível: funções lambda ou funções aninhadas (def)

Há uma vantagem de usar um lambda sobre uma função regular (eles são criados em uma expressão), e vários inconvenientes. Por essa razão, eu prefiro criar funções com a palavra chave def em vez de com lambdas.

Primeiro ponto - eles são o mesmo tipo de objeto

A lambda resultados no mesmo tipo de objeto como 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

Desde lambdas são funções, eles são objetos de primeira classe.

Ambos os lambdas e funções:

  • pode ser passado ao redor como um argumento (o mesmo que uma função regular)
  • quando criado dentro de uma função externa se tornar um fecho sobre os locais que funções externas

Mas lambdas são, por padrão, faltando algumas coisas que funções obter via sintaxe de definição de função completa.

A do lamba __name__ é '<lambda>'

Lambdas são funções anônimas, afinal, para que eles não conhecem o seu próprio nome.

>>> l.__name__
'<lambda>'
>>> foo.__name__
'foo'

Assim, de lambda não pode ser olhou para cima programaticamente em seu namespace.

Isso limita certas coisas. Por exemplo, foo pode ser olhou para cima 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 sabe seu próprio nome:

>>> pickle.loads(pickle.dumps(foo))
<function foo at 0x7fbbbee79268>

Lambdas não têm anotações e não docstring

Basicamente, lambdas não são documentados. foo reescrita Vamos ser melhor documentada:

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 dar a mesma informação para lambdas:

>>> help(l)
Help on function <lambda> in module __main__:

<lambda> lambda (...)

Mas podemos cortá-los em:

>>> l.__doc__ = 'nullary -> 0'
>>> l.__annotations__ = {'return': int}
>>> help(l)
Help on function <lambda> in module __main__:

<lambda> lambda ) -> in
    nullary -> 0

Mas há provavelmente algum erro estragar a saída de ajuda, no entanto.

Lambdas só pode retornar uma expressão

Lambdas não pode retornar instruções complexas, apenas expressões.

>>> lambda: if True: 0
  File "<stdin>", line 1
    lambda: if True: 0
             ^
SyntaxError: invalid syntax

As expressões podem reconhecidamente ser bastante complexo, e se você tentar muito duro você provavelmente pode fazer o mesmo com um lambda, mas a complexidade adicional é mais um prejuízo para escrever código claro.

Nós usamos Python para maior clareza e facilidade de manutenção. O uso excessivo de lambdas pode trabalhar contra isso.

O única de cabeça para lambdas: podem ser criados em uma única expressão

Esta é a única possível de cabeça. Desde que você pode criar um lambda com uma expressão, você pode criá-lo dentro de uma chamada de função.

Criação de uma função dentro de uma chamada de função evita o (barato) pesquisa de nome contra um criado em outro lugar.

No entanto, desde Python é estritamente avaliadas, não há outro ganho de desempenho ao fazê-lo, além de evitar a pesquisa de nome.

Para uma expressão muito simples, eu poderia escolher um lambda.

Eu também tendem a lambdas de uso ao fazer Python interativo, para evitar várias linhas quando um vai fazer. Eu uso o seguinte tipo de formato de código quando eu 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

Eu acredito que a ligeira diferença de tempo acima pode ser atribuída à pesquisa de nome no return_nullary_function -. Nota que é muito insignificante

Conclusão

Lambdas são bons para situações informais onde você quer minimizar linhas de código em favor de fazer um ponto singular.

Lambdas são ruins para situações mais formais onde você precisa de clareza para os editores de código que virão mais tarde, especialmente nos casos em que eles são não-trivial.

Sabemos que devemos dar aos nossos objetos bons nomes. Como podemos fazê-lo quando o objeto tem não nome?

Por todas estas razões, eu prefiro criar funções com def em vez de com lambda.

  • Tempo de computação.
  • Função sem nome.
  • Para alcançar uma função e funcionalidade uso muitos.

Considerando um exemplo simples,

# CREATE ONE FUNCTION AND USE IT TO PERFORM MANY OPERATIONS ON SAME TYPE OF DATA STRUCTURE.
def variousUse(a,b=lambda x:x[0]):
    return [b(i) for i in a]

dummyList = [(0,1,2,3),(4,5,6,7),(78,45,23,43)]
variousUse(dummyList)                           # extract first element
variousUse(dummyList,lambda x:[x[0],x[2],x[3]]) # extract specific indexed element
variousUse(dummyList,lambda x:x[0]+x[2])        # add specific elements
variousUse(dummyList,lambda x:x[0]*x[2])        # multiply specific elements

Se você está indo só para atribuir o lambda a uma variável no âmbito local, assim como você pode usar def porque é mais legível e pode ser expandido mais facilmente no futuro:

fun = lambda a, b: a ** b # a pointless use of lambda
map(fun, someList)

ou

def fun(a, b): return a ** b # more readable
map(fun, someList)

Um uso para lambdas eu encontrei ... está em mensagens de depuração.

Desde lambdas podem ser preguiçosamente avaliado você pode ter um código como este:

log.debug(lambda: "this is my message: %r" % (some_data,))

em vez de possivelmente caro:

log.debug("this is my message: %r" % (some_data,))

que processa a cadeia de formato mesmo se a chamada de depuração não produz saída por causa do nível de log atual.

É claro que para que ele funcione como descrito o módulo de registro em uso deve apoiar lambdas como "parâmetros preguiçosos" (como o meu módulo de registro faz).

A mesma idéia pode ser aplicada a qualquer outro caso de avaliação preguiçosa para a criação de valor conteúdo sob demanda.

Por exemplo, este operador ternário personalizado:

def mif(condition, when_true, when_false):
    if condition:
         return when_true()
    else:
         return when_false()

mif(a < b, lambda: a + a, lambda: b + b)

em vez de:

def mif(condition, when_true, when_false):
    if condition:
         return when_true
    else:
         return when_false

mif(a < b, a + a, b + b)

com lambdas apenas a expressão seleccionado pela condição serão avaliadas, sem lambdas ambos irão ser avaliados.

Claro que você pode simplesmente usar funções em vez de lambdas, mas para expressões curtas lambdas são (c) mais enxuta.

Eu concordo com nosklo. By the way, mesmo com um usar uma vez, jogue fora função, na maioria das vezes você só quer usar algo do módulo operador.

por exemplo:

Você tem uma função com essa assinatura: myFunction (dados, função de retorno)

.

Você quer passar uma função que adicionar 2 elementos.

Usando lambda:

myFunction(data, (lambda x, y : x + y))

A maneira Python:

import operator
myFunction(data, operator.add)

Ou Claro que isto é um exemplo simples, mas há um monte de coisas o módulo operador oferece, incluindo os itens setters / getters para lista e dict. Muito legal.

lambda é útil para gerar novas funções:

def somefunc(x): return lambda y: x+y
f = somefunc(10)
f(2)
>>> 12
f(4)
>>> 14

A principal diferença é que você não pode usar funções def em linha, o que na minha opinião é o caso de uso conveniente mais para uma função lambda. Por exemplo, quando ordenar uma lista de objetos:

my_list.sort(key=lambda o: o.x)

Sugiro, portanto, manter o uso de lambdas a este tipo de operações triviais, que também não realmente beneficiar a partir da documentação automática prevista nomeando a função.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top