78 votes

Pourquoi puis-je utiliser le même nom pour l'itérateur et la séquence dans une boucle for Python ?

Il s'agit plutôt d'une question conceptuelle. J'ai récemment vu un bout de code en Python (il fonctionnait en 2.7, et il se peut qu'il ait également été exécuté en 2.5) dans lequel une fonction for utilise le même nom à la fois pour la liste qui est itérée et pour l'élément de la liste, ce qui me semble être à la fois une mauvaise pratique et quelque chose qui ne devrait pas fonctionner du tout.

Par exemple :

x = [1,2,3,4,5]
for x in x:
    print x
print x

Rendement :

1
2
3
4
5
5

Maintenant, il me semble logique que la dernière valeur imprimée soit la dernière valeur assignée à x dans la boucle, mais je ne comprends pas pourquoi vous pouvez utiliser le même nom de variable pour les deux parties de l'opération. for et qu'il fonctionne comme prévu. Sont-ils dans des portées différentes ? Que se passe-t-il sous le capot pour que quelque chose comme ça puisse fonctionner ?

67voto

Sean Vieira Points 47080

Qu'est-ce que dis dites-nous :

Python 3.4.1 (default, May 19 2014, 13:10:29)
[GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from dis import dis
>>> dis("""x = [1,2,3,4,5]
... for x in x:
...     print(x)
... print(x)""")

  1           0 LOAD_CONST               0 (1)
              3 LOAD_CONST               1 (2)
              6 LOAD_CONST               2 (3)
              9 LOAD_CONST               3 (4)
             12 LOAD_CONST               4 (5)
             15 BUILD_LIST               5
             18 STORE_NAME               0 (x)

  2          21 SETUP_LOOP              24 (to 48)
             24 LOAD_NAME                0 (x)
             27 GET_ITER
        >>   28 FOR_ITER                16 (to 47)
             31 STORE_NAME               0 (x)

  3          34 LOAD_NAME                1 (print)
             37 LOAD_NAME                0 (x)
             40 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             43 POP_TOP
             44 JUMP_ABSOLUTE           28
        >>   47 POP_BLOCK

  4     >>   48 LOAD_NAME                1 (print)
             51 LOAD_NAME                0 (x)
             54 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             57 POP_TOP
             58 LOAD_CONST               5 (None)
             61 RETURN_VALUE

Les bits clés sont les sections 2 et 3 - nous chargeons la valeur à partir de x ( 24 LOAD_NAME 0 (x) ) et ensuite nous obtenons son itérateur ( 27 GET_ITER ) et commencer à itérer dessus ( 28 FOR_ITER ). Python ne revient jamais en arrière pour charger à nouveau l'itérateur .

A part : Cela n'aurait aucun sens de le faire, puisqu'il dispose déjà de l'itérateur, et comme Abhijit souligne dans sa réponse , Section 7.3 de la spécification de Python exige effectivement ce comportement).

Lorsque le nom x est écrasé pour pointer sur chaque valeur à l'intérieur de la liste anciennement connue sous le nom de x Python n'a pas de problème pour trouver l'itérateur car il n'a jamais besoin de regarder le nom. x à nouveau pour terminer le protocole d'itération.

42voto

Abhijit Points 24122

En utilisant votre exemple de code comme référence principale

x = [1,2,3,4,5]
for x in x:
    print x
print x

Je voudrais que vous vous référiez à la section 7.3. L'instruction for dans le manuel

Extrait 1

La liste d'expressions est évaluée une seule fois ; elle devrait donner un objet itérable objet. Un itérateur est créé pour le résultat de la liste d'expressions.

Ce que cela signifie, c'est que votre variable x qui est un nom symbolique d'un objet list : [1,2,3,4,5] est évalué en un objet itérable. Même si la variable, la référence symbolique change d'allégeance, comme l'indique l'objet liste d'expressions n'est pas évalué à nouveau, il n'y a pas d'impact sur l'objet itérable qui a déjà été évalué et généré.

Note

  • Tout en Python est un objet, possède un identifiant, des attributs et des méthodes.
  • Les variables sont des noms symboliques, une référence à un et un seul objet à une instance donnée.
  • Au moment de l'exécution, les variables peuvent changer d'allégeance, c'est-à-dire qu'elles peuvent faire référence à un autre objet.

Extrait 2

La suite est ensuite exécutée une fois pour chaque élément fourni par le itérateur, dans l'ordre des indices croissants.

Ici, la suite se réfère à l'itérateur et non à la liste d'expressions. Ainsi, pour chaque itération, l'itérateur est exécuté pour produire l'élément suivant au lieu de se référer à la liste d'expressions originale.

5voto

nmclean Points 4251

Il est nécessaire que cela fonctionne de cette façon, si vous y réfléchissez. L'expression pour la séquence d'un for La boucle peut être n'importe quoi :

binaryfile = open("file", "rb")
for byte in binaryfile.read(5):
    ...

Nous ne pouvons pas interroger la séquence à chaque passage de la boucle, sinon nous nous retrouverions à lire le fichier suivant lot de 5 octets la deuxième fois. Naturellement, Python doit d'une manière ou d'une autre stocker le résultat de l'expression en privé avant que la boucle ne commence.


Sont-ils dans des portées différentes ?

Non. Pour le confirmer, vous pouvez conserver une référence au dictionnaire scope original ( locals() ) et remarquez que vous utilisez en fait les mêmes variables à l'intérieur de la boucle :

x = [1,2,3,4,5]
loc = locals()
for x in x:
    print locals() is loc  # True
    print loc["x"]  # 1
    break

Qu'est-ce qui se passe sous le capot qui permet à quelque chose comme ça de de fonctionner ?

Sean Vieira a montré exactement ce qui se passe sous le capot, mais pour le décrire dans un code python plus lisible, il faut que votre for est essentiellement équivalent à ceci while boucle :

it = iter(x)
while True:
    try:
        x = it.next()
    except StopIteration:
        break
    print x

Cette approche est différente de l'approche traditionnelle de l'indexation de l'itération que l'on peut voir dans les anciennes versions de Java, par exemple :

for (int index = 0; index < x.length; index++) {
    x = x[index];
    ...
 }

Cette approche échouerait lorsque la variable de l'élément et la variable de la séquence sont les mêmes, car la séquence x ne serait plus disponible pour rechercher l'indice suivant après la première fois. x a été réaffecté au premier poste.

Avec la première approche, cependant, la première ligne ( it = iter(x) ) demande un objet itérateur qui est en fait responsable de fournir l'élément suivant à partir de ce moment-là. La séquence qui x vers lequel il était pointé à l'origine n'a plus besoin d'être accédé directement.

4voto

tdelaney Points 7235

C'est la différence entre une variable (x) et l'objet vers lequel elle pointe (la liste). Lorsque la boucle for démarre, Python saisit une référence interne à l'objet pointé par x. Il utilise l'objet et non pas ce à quoi x fait référence à un moment donné.

Si vous réassignez x, la boucle for ne change pas. Si x pointe vers un objet mutable (par exemple, une liste) et que vous modifiez cet objet (par exemple, en supprimant un élément), les résultats peuvent être imprévisibles.

3voto

ZenOfPython Points 652

En gros, la boucle for prend en compte la liste x et ensuite, le stocker comme une variable temporaire, concernant attribue un x à chaque valeur de cette variable temporaire. Ainsi , x est maintenant la dernière valeur de la liste.

>>> x = [1, 2, 3]
>>> [x for x in x]
[1, 2, 3]
>>> x
3
>>> 

Comme dans ce cas :

>>> def foo(bar):
...     return bar
... 
>>> x = [1, 2, 3]
>>> for x in foo(x):
...     print x
... 
1
2
3
>>> 

Dans cet exemple, x est stocké dans foo() comme bar Ainsi, bien que x est en cours de réaffectation, il existe toujours dans la base de données de l foo() afin que nous puissions l'utiliser pour déclencher notre for boucle.

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