48 votes

Comment est analysé `x = 42 ; x = lambda : x` ?

J'ai été surpris que cette affirmation échoue :

x = 42
x = lambda: x
assert x() == 42

Il semble que x finit par se référer récursivement à lui-même, de sorte que x() , x()() etc. sont toutes des fonctions.

Quelle est la règle utilisée pour analyser cette information, et où est-elle documentée ?

À propos (ce qui n'est pas surprenant compte tenu de ce qui précède), la valeur originale de x n'a plus de références après la définition de la lambda :

class X:
  def __del__(self): print('deleting')

x = X()
x = lambda: x  # 'deleting' is printed here

4 votes

C'est comme si def x(): return x .

45voto

molbdnilo Points 9289

La variable x est créé par la première affectation, et rebondit avec la seconde affectation.

Depuis le x dans le lambda n'est pas évalué jusqu'à ce que le lambda soit appelé, l'appel sera évalué à la valeur la plus récemment assignée.

Notez qu'il ne s'agit pas d'un scoping dynamique - s'il était dynamique, ce qui suit imprimerait "99", mais il imprime "<fonction ..." :

x = 42
x = lambda: x

def test(f):
  x = 99
  print(f())

test(x)

0 votes

Il s'agit donc d'une portée lexicale (=statique), correct ? Également pertinent : stackoverflow.com/a/51604390/336527 .

3 votes

Note : avec nonlocal x; x = 99 il hace imprimer 99.

1 votes

Ou peut-être l'exemple serait-il plus facile à comprendre avec test(lambda: x) en distinguant 42 de 99

40voto

Karl Knechtel Points 24349

La première affectation n'est pas pertinente ; la x dans le corps de la lambda es lié tardivement :

x = lambda: x # no need for a prior assignment
x = lambda: y # notice: no NameError occurs, *until it is called*

C'est la même raison pour laquelle la création de lambdas dans une boucle est délicate et est également utilisé pour faire des arbres avec la bibliothèque standard defaultdict :

tree = lambda: defaultdict(tree)
t = tree()
t['foo']['bar']['baz'] = 'look ma, no intermediate steps'

14voto

tdelaney Points 7235

Un lambda est un objet fonction anonyme. Python résout complètement tout ce qui se trouve du côté droit d'une équation en un seul objet anonyme, puis résout tout ce qui se trouve du côté gauche pour l'affectation.

x = lambda: x

compile d'abord lambda: x en un objet fonction qui renvoie ce qui se trouve dans x au moment où il est appelé. Il rebondit ensuite x avec cet objet fonction, en supprimant tout objet qui se trouvait là avant.

Maintenant x est une fonction qui renvoie ce qui se trouve dans x ... qui est une fonction qui renvoie tout ce qui se trouve dans x etc... Vous pouvez donc écrire x()()()()()() autant de fois que tu veux, et tu obtiendras toujours l'original. lambda:x objet de la fonction.

Les fonctions Python possèdent un espace de noms local, mais seules les variables affectées dans la fonction y résident. Puisque x n'est pas attribué dans le lambda il est résolu dans la portée qui le contient, c'est-à-dire au niveau du module "x". Un morceau de code identique est

def x():
    return x

Comparez cela avec

def x():
    x = 1
    return x

Maintenant, le paramètre x est une variable locale et n'est pas liée à la variable globale x .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X