web-dev-qa-db-fra.com

Portée de la variable python dans la boucle for

Heres the code python im ayant des problèmes avec:

for i in range (0,10):
    if i==5:
        i+=3
    print i

Je m'attendais à ce que le résultat soit:

0
1
2
3
4
8
9

cependant l'interprète recrache:

0
1
2
3
4
8
6
7
8
9

Je sais qu'une boucle for crée une nouvelle portée pour une variable en C, mais je n'ai aucune idée de python. Quelqu'un peut-il expliquer pourquoi la valeur de i ne change pas dans la boucle for en python et quelle est la solution pour obtenir le résultat attendu?.

30
firecast

La boucle for parcourt tous les nombres dans range(10), c'est-à-dire [0,1,2,3,4,5,6,7,8,9].
Le fait de changer la valeur current de i n'a aucun effet sur la valeur suivante de la plage.

Vous pouvez obtenir le comportement souhaité avec une boucle while.

i = 0
while i < 10:
    # do stuff and manipulate `i` as much as you like       
    if i==5:
        i+=3

    print i

    # don't forget to increment `i` manually
    i += 1
32
Junuxx

Analogie avec le code C

Vous imaginez que votre for-loop en python ressemble à ce code C:

for (int i = 0; i < 10; i++)
    if (i == 5)
        i += 3;

Cela ressemble plus à ce code C:

int r[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (int j = 0; j < sizeof(r)/sizeof(r[0]); j++) {
    int i = r[j];
    if (i == 5)
        i += 3;
}

Donc, modifier i dans la boucle n'a pas l'effet escompté.

Exemple de démontage

Vous pouvez regarder le désassemblage du code python pour voir ceci:

>>> from dis import dis
>>> def foo():
...     for i in range (0,10):
...         if i==5:
...             i+=3
...         print i
... 
>>> dis(foo)
  2           0 SETUP_LOOP              53 (to 56)
              3 LOAD_GLOBAL              0 (range)
              6 LOAD_CONST               1 (0)
              9 LOAD_CONST               2 (10)
             12 CALL_FUNCTION            2
             15 GET_ITER            
        >>   16 FOR_ITER                36 (to 55)
             19 STORE_FAST               0 (i)

  3          22 LOAD_FAST                0 (i)
             25 LOAD_CONST               3 (5)
             28 COMPARE_OP               2 (==)
             31 POP_JUMP_IF_FALSE       47

  4          34 LOAD_FAST                0 (i)
             37 LOAD_CONST               4 (3)
             40 INPLACE_ADD         
             41 STORE_FAST               0 (i)
             44 JUMP_FORWARD             0 (to 47)

  5     >>   47 LOAD_FAST                0 (i)
             50 PRINT_ITEM          
             51 PRINT_NEWLINE       
             52 JUMP_ABSOLUTE           16
        >>   55 POP_BLOCK           
        >>   56 LOAD_CONST               0 (None)
             59 RETURN_VALUE        
>>> 

Cette partie crée une plage comprise entre 0 et 10 et la réalise:

          3 LOAD_GLOBAL              0 (range)
          6 LOAD_CONST               1 (0)
          9 LOAD_CONST               2 (10)
         12 CALL_FUNCTION            2

À ce stade, le haut de la pile contient la plage.

Ceci obtient un itérateur sur l’objet au sommet de la pile , c’est-à-dire la plage:

         15 GET_ITER  

À ce stade, le haut de la pile contient un itérateur sur la plage réalisée.

FOR_ITER commence à parcourir la boucle en utilisant l'itérateur en haut de la pile:

    >>   16 FOR_ITER                36 (to 55)

À ce stade, le haut de la pile contient la valeur suivante de l'itérateur.

Et ici vous pouvez voir que le haut de la pile est sauté et assigné à i :

         19 STORE_FAST               0 (i)

Donc i sera écrasé indépendamment de ce que vous ferez dans la boucle.

Voici un aperçu des machines de la pile si vous ne l'avez pas déjà vu auparavant.

17
hughdbrown

Une boucle for en Python est en fait une boucle for-each. Au début de chaque boucle, i est défini sur l'élément suivant de l'itérateur (range(0, 10) dans votre cas). La valeur de i est réinitialisée au début de chaque boucle. Par conséquent, sa modification dans le corps de la boucle ne modifie pas sa valeur pour la prochaine itération.

Autrement dit, la boucle for que vous avez écrite est équivalente à la boucle while suivante:

_numbers = range(0, 10) #the list [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
_iter = iter(_numbers)
while True:
    try:
        i = _iter.next()
    except StopIteration:
        break

    #--YOUR CODE HERE:--
    if i==5:
        i+=3
    print i
14
Claudiu

Si pour une raison quelconque vous vouliez vraiment modifier ajouter 3 à i quand il est égal à 5, et ignorer les éléments suivants (c'est un peu comme faire avancer le pointeur dans les éléments C 3), alors vous pouvez utiliser un itérateur et consommer quelques bits à partir de ce:

from collections import deque
from itertools import islice

x = iter(range(10)) # create iterator over list, so we can skip unnecessary bits
for i in x:
    if i == 5:             
        deque(islice(x, 3), 0) # "swallow up" next 3 items
        i += 3 # modify current i to be 8
    print i

0
1
2
3
4
8
9
4
Jon Clements

Dans python 2.7, la fonction range crée une liste, tandis que dans les versions 3.x de python, elle crée un objet de classe 'range' qui est seulement itérable, pas une liste, similaire à xrange dans python 2.7.

Pas lorsque vous parcourez la plage (1, 10), vous lisez éventuellement à partir de l'objet de type liste et i prend une nouvelle valeur chaque fois qu'il atteint la boucle.

c'est quelque chose comme:

for i in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]:
    if i==5:
        i+=3
    print(i)

Changer la valeur ne changera pas l'ordre des itérations dans la liste.

2
pranav dua
it = iter(xrange (0,10))
for i in it:
    if i==4: all(it.next() for a in xrange(3))
    print i

ou

it = iter(xrange (0,10))
itn = it.next
for i in it:
    if i==4: all(itn() for a in xrange(3))
    print i
1
eyquem

Je suis réinitialisé à chaque itération, donc peu importe ce que vous faites dans la boucle. La seule fois où il fait quoi que ce soit, c'est quand i est 5, et il ajoute 3 à cela. Une fois qu'il reboucle, il revient ensuite au numéro suivant de la liste. Vous voudrez probablement utiliser une while ici.

1
Hoopdady

La boucle for de Python parcourt simplement la séquence de valeurs fournie - pensez-la comme "foreach". Pour cette raison, la modification de la variable n'a aucun effet sur l'exécution de la boucle.

Ceci est bien décrit dans le tutoriel .

1
user4815162342

Vous pouvez apporter les modifications suivantes à votre boucle for:

for i in range (0,10):
    if i in [5, 6, 7]:
        continue
    print(i)
0
CopyPasteIt

À mon avis, le code analogue n'est pas une boucle while, mais une boucle for dans laquelle vous modifiez la liste pendant l'exécution:

originalLoopRange = 5
loopList = list(range(originalLoopRange))
timesThroughLoop = 0
for loopIndex in loopList:
    print(timesThroughLoop, "count")
    if loopIndex == 2:
        loopList.pop(3)
        print(loopList)
    print(loopIndex)
    timesThroughLoop += 1
0
AdityaS