Comportement de l'itérateur de liste Python et suivant (itérateur)

147

Considérer:

>>> lst = iter([1,2,3])
>>> next(lst)
1
>>> next(lst)
2

Ainsi, l'avancement de l'itérateur est, comme prévu, géré par la mutation de ce même objet.

Cela étant le cas, je m'attendrais à:

a = iter(list(range(10)))
for i in a:
   print(i)
   next(a)

pour sauter tous les deux éléments: l'appel à nextdevrait avancer l'itérateur une fois, puis l'appel implicite effectué par la boucle devrait l'avancer une deuxième fois - et le résultat de ce deuxième appel serait assigné à i.

Ce n'est pas le cas. La boucle imprime tous les éléments de la liste, sans en sauter aucun.

Ma première pensée a été que cela pourrait se produire parce que la boucle fait appel iterà ce qu'elle est passé, et cela pourrait donner un itérateur indépendant - ce n'est pas le cas, comme nous l'avons fait iter(a) is a.

Alors, pourquoi ne nextsemble pas faire avancer l'itérateur dans ce cas?

lvc
la source

Réponses:

197

Ce que vous voyez est l' interpréteur qui renvoie la valeur de retour de next()en plus d' iêtre imprimé à chaque itération:

>>> a = iter(list(range(10)))
>>> for i in a:
...    print(i)
...    next(a)
... 
0
1
2
3
4
5
6
7
8
9

Il en 0va de même pour la sortie de print(i), 1la valeur de retour de next(), renvoyée par l'interpréteur interactif, etc. Il n'y a que 5 itérations, chaque itération entraînant l'écriture de 2 lignes dans le terminal.

Si vous affectez la sortie des next()choses fonctionnent comme prévu:

>>> a = iter(list(range(10)))
>>> for i in a:
...    print(i)
...    _ = next(a)
... 
0
2
4
6
8

ou imprimez des informations supplémentaires pour différencier la print()sortie de l'écho de l'interpréteur interactif:

>>> a = iter(list(range(10)))
>>> for i in a:
...    print('Printing: {}'.format(i))
...    next(a)
... 
Printing: 0
1
Printing: 2
3
Printing: 4
5
Printing: 6
7
Printing: 8
9

En d'autres termes, next()fonctionne comme prévu, mais comme il renvoie la valeur suivante de l'itérateur, répercutée par l'interpréteur interactif, vous êtes amené à croire que la boucle a en quelque sorte sa propre copie d'itérateur.

Martijn Pieters
la source
13
Je n'étais pas au courant de ce comportement de la part de l'interprète. Je suis heureux d'avoir découvert cela avant de perdre beaucoup de temps à m'interroger à ce sujet tout en résolvant un problème réel.
brandizzi
5
... *meurt*. Le pire, c'est que je me souviens avoir mentionné exactement ce comportement d'interprète à quelqu'un il y a peut-être une semaine.
lvc
intéressant. J'ai essayé pour i dans a: next (a); print i et j'ai pensé que je passerais à 1 et imprimerais 1,3,5,7,9. Mais encore, c'est 0,2,4,6,8. Pourquoi?
user2290820
3
ia déjà été attribué. next(a)signifie que la prochaine itération 2est assignée i, puis vous vous déplacez à anouveau, imprimez i, etc.
Martijn Pieters
1
Cela ne fonctionne pas si n est impair - StopIterationexceptionnellement, il next(a)est déclenché lorsqu'il est appelé après que la liste est épuisée.
Raf
13

Ce qui se passe, c'est que next(a)renvoie la valeur suivante de a, qui est imprimée sur la console car elle n'est pas affectée.

Ce que vous pouvez faire, c'est affecter une variable avec cette valeur:

>>> a = iter(list(range(10)))
>>> for i in a:
...    print(i)
...    b=next(a)
...
0
2
4
6
8
njzk2
la source
8

Je trouve les réponses existantes un peu déroutant, car ils ne montrent indirectement la chose mystifiant essentiel dans l'exemple de code: à la fois * le « print i » et la « prochaine (a) » sont à l' origine de leurs résultats à imprimer.

Puisqu'ils impriment des éléments alternés de la séquence originale, et qu'il est inattendu que l'instruction "next (a)" s'imprime, il semble que l'instruction "print i" imprime toutes les valeurs.

Dans cette optique, il devient plus clair que l'affectation du résultat de "next (a)" à une variable empêche l'impression de son résultat, de sorte que seules les valeurs alternatives que la variable de boucle "i" sont imprimées. De même, le fait de faire émettre à l'instruction "print" quelque chose de plus distinctif l'élimine également.

(L'une des réponses existantes réfute les autres parce que cette réponse consiste à évaluer l'exemple de code sous forme de bloc, de sorte que l'interpréteur ne rapporte pas les valeurs intermédiaires pour "next (a)".)

La chose séduisante en répondant aux questions, en général, est d'être explicite sur ce qui est évident une fois que vous connaissez la réponse. Cela peut être insaisissable. De même, critiquez les réponses une fois que vous les avez comprises. C'est intéressant...

klm
la source
2

Quelque chose ne va pas avec votre Python / ordinateur.

a = iter(list(range(10)))
for i in a:
   print(i)
   next(a)

>>> 
0
2
4
6
8

Fonctionne comme prévu.

Testé en Python 2.7 et en Python 3+. Fonctionne correctement dans les deux

Rose Inbar
la source
5
J'obtiens le même résultat que @lvc (uniquement sur IDLE cependant, lorsqu'il est exécuté en tant que script, j'obtiens ceci))
jamylak
3
@Inbar Rose Uniquement si vous exécutez en tant que script.
Quintec
1
c'est le comportement de mettre du code via un shell interactif. Si la fonction retourne la valeur sans être utilisée, l'interpréteur l'afficherait sur le shell en tant que sortie de débogage
Reishin
2

Pour ceux qui ne comprennent toujours pas.

>>> a = iter(list(range(10)))
>>> for i in a:
...    print(i)
...    next(a)
... 
0 # print(i) printed this
1 # next(a) printed this
2 # print(i) printed this
3 # next(a) printed this
4 # print(i) printed this
5 # next(a) printed this
6 # print(i) printed this
7 # next(a) printed this
8 # print(i) printed this
9 # next(a) printed this

Comme d'autres l'ont déjà dit, nextaugmente l'itérateur de 1 comme prévu. Assigner sa valeur retournée à une variable ne change pas son comportement par magie.

Wesley
la source
1

Il se comporte comme vous le souhaitez s'il est appelé en tant que fonction:

>>> def test():
...     a = iter(list(range(10)))
...     for i in a:
...         print(i)
...         next(a)
... 
>>> test()
0
2
4
6
8
burmer
la source