Por resultados do mapa () e compreensão da lista são diferentes?
-
02-07-2019 - |
Pergunta
O seguinte teste falhar:
#!/usr/bin/env python
def f(*args):
"""
>>> t = 1, -1
>>> f(*map(lambda i: lambda: i, t))
[1, -1]
>>> f(*(lambda: i for i in t)) # -> [-1, -1]
[1, -1]
>>> f(*[lambda: i for i in t]) # -> [-1, -1]
[1, -1]
"""
alist = [a() for a in args]
print(alist)
if __name__ == '__main__':
import doctest; doctest.testmod()
Em outras palavras:
>>> t = 1, -1
>>> args = []
>>> for i in t:
... args.append(lambda: i)
...
>>> map(lambda a: a(), args)
[-1, -1]
>>> args = []
>>> for i in t:
... args.append((lambda i: lambda: i)(i))
...
>>> map(lambda a: a(), args)
[1, -1]
>>> args = []
>>> for i in t:
... args.append(lambda i=i: i)
...
>>> map(lambda a: a(), args)
[1, -1]
Solução
Eles são diferentes, porque o valor de i
tanto no gerador de expressão ea lista comp são avaliados preguiçosamente, ou seja, quando as funções anônimas são invocados em f
.
Por esse tempo, i
está vinculado ao último valor se t
, que é -1.
Então, basicamente, é isso que a compreensão da lista faz (da mesma forma para o genexp):
x = []
i = 1 # 1. from t
x.append(lambda: i)
i = -1 # 2. from t
x.append(lambda: i)
Agora, os lambdas levar em torno de um fecho que as referências i
, mas i
é obrigado a -1 em ambos os casos, porque esse é o último valor que foi atribuído a.
Se você quiser ter certeza de que o lambda recebe o valor atual de i
, faça
f(*[lambda u=i: u for i in t])
Desta forma, você forçar a avaliação de i
no momento do encerramento é criado.
Outras dicas
As variáveis ??capta lambda, não valores, daí o código
lambda : i
sempre retornará o valor i é atualmente obrigado a no fechamento. No momento em que é chamado, esse valor foi definido para -1.
Para obter o que deseja, você vai precisar para capturar o real vinculativo no momento do lambda é criado, por:
>>> f(*(lambda i=i: i for i in t)) # -> [-1, -1]
[1, -1]
>>> f(*[lambda i=i: i for i in t]) # -> [-1, -1]
[1, -1]
Expression f = lambda: i
é equivalente a:
def f():
return i
Expression g = lambda i=i: i
é equivalente a:
def g(i=i):
return i
i
é um variável livre no primeiro caso, e é ligado ao parâmetro função no segundo caso, isto é, é uma variável local, nesse caso. Os valores para os parâmetros padrão são avaliados no momento da definição da função.
gerador de expressão é o escopo mais próxima envolvente (onde i
é definido) para o nome i
na expressão lambda
, portanto i
é resolvido pelo facto de bloco:
f(*(lambda: i for i in (1, -1)) # -> [-1, -1]
i
é uma variável local do bloco lambda i: ...
, por conseguinte, o objecto que se refere é definida no referido bloco:
f(*map(lambda i: lambda: i, (1,-1))) # -> [1, -1]