2009-07-10 6 views
14

Que se passe-t-il ici? J'essaye de créer une liste de fonctions:Problèmes Lambda Python

def f(a,b): 
    return a*b 

funcs = [] 

for i in range(0,10): 
    funcs.append(lambda x:f(i,x)) 

Ceci ne fait pas ce que j'attends. Je me attends à la liste d'agir comme ceci:

funcs[3](3) = 9 
funcs[0](5) = 0 

Mais toutes les fonctions de la liste semblent être identiques, et établirons la valeur fixe soit 9:

funcs[3](3) = 27 
funcs[3](1) = 9 

funcs[2](6) = 54 

Toutes les idées?

Répondre

18

lambdas en python sont fermetures .... les arguments que vous lui donnez aren » t va être évalué jusqu'à ce que le lambda soit évalué. À ce moment-là, je suis 9, peu importe, parce que votre itération est terminée.

Le comportement que vous cherchez peut être réalisé avec functools.partial

import functools 

def f(a,b): 
    return a*b 

funcs = [] 

for i in range(0,10): 
    funcs.append(functools.partial(f,i)) 
+0

Cela devrait être functools.partial (f, i) – FogleBird

+0

Je suis d'accord.Une application partielle est le moyen d'aller ici. –

+0

ici partiel (f, i) signifie partiel (f, b = i) non partiel (f, a = i). ce n'est donc pas la même chose que le message original. L'application de fonction partielle 'de la droite' (http://www.gossamer-threads.com/lists/python/dev/715103) a été rejetée deux fois. – sunqiang

2

Compte tenu de la valeur finale de i == 9

Comme toute bonne fonction python, il va utiliser la valeur de la variable dans la portée qu'elle a été définie. Peut-être lambda: varname (étant que c'est une construction de langage) se lie au nom, pas à la valeur, et évalue ce nom à l'exécution?

similaires à:

i = 9 
def foo(): 
    print i 

i = 10 
foo() 

Je serais très intéressé à trouver de ma réponse est correcte

10

Il n'y a qu'une seule i qui est lié à chaque lambda, contrairement à ce que vous pensez. C'est une erreur commune.

Une façon d'obtenir ce que vous voulez est:

for i in range(0,10): 
    funcs.append(lambda x, i=i: f(i, x)) 

Maintenant, vous créez un paramètre par défaut i dans chaque fermeture lambda et se liant à elle la valeur actuelle de la variable boucle i.

13

Oui, le "problème de portée" habituel (en fait un problème de liaison-plus-que-vous voulez, mais il est souvent appelé par ce nom). Vous avez déjà obtenu les deux meilleurs réponses (parce que plus simple) - le « défaut faux » i=i solution, et functools.partial, donc je ne fais que donner le troisième des trois classiques, le « lambda usine »:

for i in range(0,10): 
    funcs.append((lambda i: lambda x: f(i, x))(i)) 

Personnellement j'irais avec i=i s'il n'y a aucun risque que les fonctions de funcs soient accidentellement appelées avec 2 paramètres au lieu d'un seul, mais l'approche de la fonction d'usine mérite d'être considérée lorsque vous avez besoin de quelque chose d'un peu plus riche que liant un arg.

+2

Je n'ai pas vu celui-ci avant. Genre de soignée. – ars

+0

@ars, un peu exagéré pour la plupart des cas (en Python), mais c'est ce que l'on ferait naturellement dans (disons) Scheme, donc je suppose que c'est un peu soigné ;-). –

+0

À droite, je ne peux pas immédiatement penser à un cas où je le préférerais, mais une construction amusante néanmoins. :-) – ars