28 votes

En Python, pourquoi une expression lambda peut-elle faire référence à la variable en cours de définition mais pas à une liste?

C'est plus une curiosité que rien, mais je viens de remarquer ce qui suit. Si je suis la définition d'une auto-référentielle lambda, je peux le faire facilement:

>>> f = lambda: f
>>> f() is f
True

Mais si je suis la définition d'une auto-référentielle de la liste, je dois le faire en plus d'une instruction:

>>> a = [a]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
>>> a = []
>>> a.append(a)
>>> a[0] is a
True
>>> a
[[...]]

J'ai aussi remarqué que ce n'est pas limité à des listes, mais semble comme toute autre expression autre qu'un lambda ne peut pas référencer la variable à gauche de l'affectation. Par exemple, si vous avez un cyclique de liste liée à un nœud, vous ne pouvez pas simplement aller:

>>> class Node(object):
...     def __init__(self, next_node):
...         self.next = next_node
... 
>>> n = Node(n)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'n' is not defined

Au lieu de cela, vous devez le faire en deux états:

>>> n = Node(None)
>>> n.next = n
>>> n is n.next
True

Personne ne sait ce que la philosophie derrière cette différence est? Je comprends que récursive lambda sont utilisés beaucoup plus fréquemment, et donc de soutien à l'auto-référence est important pour les lambdas, mais pourquoi ne pas l'autoriser pour toute cession?

EDIT: Les réponses ci-dessous clarifier ce assez bien. La raison en est que les variables dans les expressions lambda en Python sont évalués à chaque fois le lambda est appelé, non pas lorsqu'il est défini. En ce sens, ils sont exactement comme les fonctions définies à l'aide de def. J'ai écrit le code suivant à expérimenter avec la façon dont cela fonctionne, à la fois avec les lambdas et def fonctions dans le cas où il pourrait aider à clarifier pour tout le monde.

>>> f = lambda: f
>>> f() is f
True
>>> g = f
>>> f = "something else"
>>> g()
'something else'
>>> f = "hello"
>>> g()
'hello'
>>> f = g
>>> g() is f
True

>>> def f():
...     print(f)
... 
>>> f()
<function f at 0x10d125560>
>>> g = f
>>> g()
<function f at 0x10d125560>
>>> f = "test"
>>> g()
test
>>> f = "something else"
>>> g()
something else

25voto

iCodez Points 41884

L'expression à l'intérieur d'un lambda est évaluée lorsque la fonction est appelée, non pas lorsqu'elle est définie.

En d'autres termes, Python n'évaluera pas l' f à l'intérieur de votre lambda jusqu'à ce que vous l'appelez. Et, par la suite, f est déjà défini dans le champ d'application actuel (c'est le lambda lui-même). Donc, pas de NameError est élevé.


Notez que ce n'est pas le cas pour une ligne comme ceci:

a = [a]  

Quand Python interprète ce type de ligne (connu comme une instruction d'affectation), il permettra d'évaluer l'expression à droite de l' = immédiatement. En outre, un NameError sera porté pour n'importe quel nom utilisé sur la droite qui n'est pas défini dans le champ d'application actuel.

9voto

BrenBarn Points 63718

Parce qu'un lambda est une fonction et que le corps de la fonction n'est pas exécuté tant que la fonction n'est pas appelée.

En d'autres termes, l'autre façon de procéder est la suivante:

 def f():
    return f
 

Mais vous avez raison de ne pas pouvoir le faire dans une expression car def est une instruction, donc elle ne peut pas être utilisée dans une expression.

4voto

Aaron Hall Points 7381

Nous pouvons nous voir quand nous démonter la fonction lambda (c'est à l'identique de sortie en Python 2.6 et 3.3)

>>> import dis
>>> f = lambda: f
>>> dis.dis(f)
  1           0 LOAD_GLOBAL              0 (f)
              3 RETURN_VALUE

Nous démontrons que nous n'avons pas besoin de charger f jusqu'à ce qu'elle est appelée, après quoi il est déjà défini à l'échelle mondiale, et donc stockés, de sorte que cela fonctionne:

>>> f is f()
True

Mais quand nous le faisons:

>>> a = [a]    

Nous avons un message d'erreur (si a est déjà pas défini), et si nous démonter Python de la mise en œuvre de cette.

>>> def foo():
...     a = [a]
...     
>>> dis.dis(foo)
  2           0 LOAD_FAST                0 (a)
              3 BUILD_LIST               1
              6 STORE_FAST               0 (a)
              9 LOAD_CONST               0 (None)
             12 RETURN_VALUE    

nous voyons que nous devons tenter de charger a avant que nous avons enregistrées.

2voto

Corley Brigman Points 3545

Il n'y a rien de spécial à-boîtier requis pour ce faire; c'est juste la façon dont il fonctionne.

Une expression lambda n'est pas différente d'une fonction normale, vraiment. Sens, je peux faire ceci:

x = 1
def f():
    print x + 2
f()
3
x = 2
f()
4

Comme vous pouvez le voir, à l'intérieur de la fonction, la valeur de x n'ont pas une valeur prédéfinie - il leva les yeux lorsque nous exécutons f. Cela comprend la valeur de la fonction elle-même: Nous ne regardons pas ce qu' f représente jusqu'à ce que nous avons fait exécuter, et d'ici là, il existe.

D'en faire un lambda ne fonctionne pas différemment:

del x
f = lambda: x+2
f()
NameError: global name 'x' is not defined
x = 2
f()
4

fonctionne de la même façon. Dans ce cas, je suis allé de l'avant et supprimé x de sorte qu'il n'était plus dans le champ d'application lors de l' f a été défini, et en cours d'exécution f dans ce cas montre bien qu' x n'existe pas. Mais après nous définissons x, alors f fonctionne à nouveau.

Ce qui est différent dans la liste de cas, parce que nous sommes la génération d'un objet droit maintenant, et donc tout sur le côté droit doit être lié, dès maintenant. La façon python fonctionne (ce que je comprends, et au moins dans la pratique, cela a été utile) est de considérer que tout sur le côté droit est deferenced & bound et ensuite traitées, et seulement après que tous sont les valeur(s) sur le côté gauche lié et affecté.

Depuis la même valeur sur le côté droit et le gauche, lors de python essaie de lier le nom sur le côté droit, il n'existe pas encore.

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