Por que a saída das duas compreensões de lista a seguir é diferente, embora f
a lambda
função seja a mesma?
f = lambda x: x*x
[f(x) for x in range(10)]
e
[lambda x: x*x for x in range(10)]
Lembre-se, ambos type(f)
e type(lambda x: x*x)
retorne o mesmo tipo.
[lambda x: x*x for x in range(10)]
é mais rápido que o primeiro, pois não chama uma função de loop externo, f repetidamente.[x*x for x in range(10)]
é melhor.Respostas:
O primeiro cria uma única função lambda e a chama dez vezes.
O segundo não chama a função. Cria 10 funções lambda diferentes. Coloca todos aqueles em uma lista. Para torná-lo equivalente ao primeiro, você precisa:
Ou melhor ainda:
fonte
map(lambda x: x*x, range(10))
, provavelmente, o que o OP significava em primeiro lugar.list(map(lambda x: x*x, range(10)))
, dará a você[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Essa pergunta toca uma parte muito fedorenta da sintaxe "famosa" e "óbvia" do Python - o que tem precedência, a lambda ou a compreensão da lista.
Não acho que o objetivo do OP fosse gerar uma lista de quadrados de 0 a 9. Se esse fosse o caso, poderíamos dar ainda mais soluções:
Mas não é esse o ponto. O ponto é W (hy) TF. Essa expressão ambígua é tão contra-intuitiva? E eu tenho um caso idiota para você no final, então não descarte minha resposta muito cedo (eu a tive em uma entrevista de emprego).
Portanto, a compreensão do OP retornou uma lista de lambdas:
Obviamente, são apenas 10 cópias diferentes da função quadratura, veja:
Observe os endereços de memória dos lambdas - todos são diferentes!
Obviamente, você poderia ter uma versão mais "ótima" (haha) dessa expressão:
Vejo? 3 vezes a mesma lambda.
Por favor, note que eu usei
_
comofor
variável. Não tem nada a ver com ox
nolambda
(é ofuscado lexicamente!). Pegue?Estou deixando de fora a discussão, por que a precedência da sintaxe não é assim, e tudo isso significava:
que poderia ser:,
[[0, 1, 4, ..., 81]]
ou[(0, 1, 4, ..., 81)]
, ou que eu acho mais lógico , esse seria umlist
elemento de 1 - umgenerator
retorno dos valores. Simplesmente não é o caso, a linguagem não funciona dessa maneira.MAS o que, se ...
E se você não ofuscar a
for
variável, e usá-lo no seulambda
s ??Bem, então porcaria acontece. Veja isso:
isto significa, é claro:
MAS NÃO significa:
Isso é loucura!
As lambdas na compreensão da lista são um fechamento sobre o escopo dessa compreensão. Um fechamento lexical , para que eles se refiram à
i
referência via, e não ao seu valor quando foram avaliados!Então, esta expressão:
É aproximadamente equivalente a:
Tenho certeza de que poderíamos ver mais aqui usando um descompilador python (com o que quero dizer, por exemplo, o
dis
módulo), mas para a discussão independente de Python-VM isso é suficiente. Tanta coisa para a pergunta da entrevista de emprego.Agora, como fazer um
list
multiplicador de lambdas, que realmente se multiplica por números inteiros consecutivos? Bem, da mesma forma que a resposta aceita, precisamos quebrar o vínculo diretoi
envolvendo-o em outrolambda
, que está sendo chamado dentro da expressão de compreensão da lista:Antes:
Depois de:
(Eu também tinha a variável lambda externa = =
i
, mas decidi que essa é a solução mais clara - apresenteiy
para que todos possamos ver qual bruxa é qual).Editar 2019-08-30:
Seguindo uma sugestão de @josoler, que também está presente em uma resposta de @sheridp - o valor da "variável de loop" de compreensão da lista pode ser "incorporado" dentro de um objeto - a chave é que ele seja acessado no momento certo. A seção "Depois" acima faz isso envolvendo-o em outro
lambda
e chamando-o imediatamente com o valor atual dei
. Outra maneira (um pouco mais fácil de ler - não produz efeito 'WAT') é armazenar o valor dei
dentro de umpartial
objeto e fazer com que o "interno" (original) olambda
leve como argumento (transmitido pelopartial
objeto no hora da chamada), ou seja:Após 2:
Ótimo, mas ainda há uma pequena reviravolta para você! Digamos que não queremos facilitar o leitor de código e passar o fator pelo nome (como argumento de palavra-chave
partial
). Vamos fazer uma renomeação:Após 2,5:
WAT?
Espere ... Estamos alterando o número de argumentos por 1 e passando de "muitos" para "poucos"?
Bem, não é uma verdadeira WAT, quando passamos
coef
parapartial
, desta forma, torna-se um argumento de palavra-chave, por isso deve vir após o posicionalx
argumento, assim:Após 3:
Eu preferiria a última versão sobre o lambda aninhado, mas cada um na sua ...
fonte
[partial(lambda i, x: i * x, i) for i in (1, 2)]
A grande diferença é que o primeiro exemplo realmente chama o lambda
f(x)
, enquanto o segundo exemplo não.Seu primeiro exemplo é equivalente a
[(lambda x: x*x)(x) for x in range(10)]
enquanto o segundo exemplo é equivalente a[f for x in range(10)]
.fonte
O primeiro
é executado
f()
para cada valor no intervalo, assim comof(x)
para cada valoro segundo
executa o lambda para cada valor na lista, portanto, gera todas essas funções.
fonte
As pessoas deram boas respostas, mas esqueceram de mencionar a parte mais importante na minha opinião: No segundo exemplo, a
X
compreensão da lista NÃO é a mesmaX
dalambda
função, elas são totalmente independentes. Portanto, o segundo exemplo é realmente o mesmo que:As iterações internas ativadas
range(10)
são responsáveis apenas pela criação de 10 funções lambda semelhantes em uma lista (10 funções separadas, mas totalmente similares - retornando a potência 2 de cada entrada).Por outro lado, o primeiro exemplo funciona totalmente diferente, porque o X das iterações interage com os resultados, para cada iteração o valor é,
X*X
portanto, o resultado seria[0,1,4,9,16,25, 36, 49, 64 ,81]
fonte
As outras respostas estão corretas, mas se você estiver tentando fazer uma lista de funções, cada uma com um parâmetro diferente, que pode ser executado posteriormente , o seguinte código fará isso:
Enquanto o exemplo é artificial, achei útil quando eu queria uma lista de funções em que cada uma imprimisse algo diferente, ou seja,
fonte