Création de fonctions dans une boucle

J'essaie de créer des fonctions à l'intérieur d'une boucle et de les stocker dans un dictionnaire. Le problème est que toutes les entrées du dictionnaire semblent finir par correspondre à la dernière fonction créée. Le code va comme ceci:

d = {}
def test(**kwargs):
    for k in kwargs:
        def f():
            print(k, kwargs[k])
        d[k] = f
        f()

test(foo=1, bar=2)
print('should print the same output as before')
d['foo']()
d['bar']()

Cette sortie:

foo 1
bar 2
should print the same output as before
bar 2
bar 2

Une idée de pourquoi?

48
demandé sur Aran-Fey 2010-08-07 23:04:33

1 réponses

Vous rencontrez un problème avec late binding - chaque fonction recherche k le plus tard possible (ainsi, lorsqu'elle est appelée en dehors de test, cela se produit après la fin de la boucle).

Facilement fixé en forçant la liaison précoce: changez def f(): en def f(k=k): comme ceci:

def f(k=k):
    print(k, kwargs[k])

Les valeurs par Défaut (la droite k dans k=k est une valeur par défaut pour le nom d'argument k, ce qui est la gauche k dans k=k) sont regardé def, non pas pendant l' call temps, donc, essentiellement, ils sont un moyen de rechercher spécifiquement la liaison précoce.

Si vous êtes inquiet à propos de f obtenir un argument supplémentaire (et donc potentiellement être appelé à tort), il y a une manière plus sophistiquée qui implique l'utilisation d'une fermeture comme une "usine de fonctions":

def make_f(kwargs, k):
    def f():
        print(k, kwargs[k])
    return f

Et dans votre boucle utilisez f = make_f(kwargs, k) au lieu de l'instruction def.

97
répondu Alex Martelli 2018-08-10 11:05:13