4 votes

Itération et mise à jour de la liste en python

Je n'arrive pas à comprendre pourquoi les éléments suivants indéfinie (alors que je n'utilise pas la liste de copie)

list = ["Mohit","kumar","sffsfshfsd"]
for w in list:
    if(len(w)) > 5:
        list.insert(0,w)
    print("inside loop")

print(list)  

Le code ci-dessus imprime boucle intérieure indéfiniment.

Maintenant, si à la place de la liste, j'utilise une liste de copie comme ci-dessous, cela fonctionne bien.

list = ["mohit","kumar","sffffgssddf"]

for w in list[:]:
    if len(w) > 5:
        list.insert(0,w)
    print("inside loop")

print(list)  

J'ai lu dans la documentation de python que c'est le comportement à adopter mais je veux comprendre la raison de ce comportement. Merci d'avance. l'avance.

5voto

Bilal Akil Points 3440

La première boucle for for w in list utilisera un itérateur (de iter(list) ) pour récupérer et parcourir en boucle chaque élément de la liste. Cet itérateur ne récupère pas immédiatement la liste entière - il est paresseux ce qui signifie qu'il ne prend qu'un seul élément à la fois dans la liste, lorsqu'il en a besoin. Vous pouvez apprendre à connaître le protocole d'itération ici ou itération/générateurs et paresse ici .

Boucler sur les index 0 et 1 ne donne rien, car leurs longueurs de chaîne sont inférieures à 6. À l'indice 2, cependant, vous ajoutez "sffsfshfsd" au début de list . Maintenant list a grandi et il y a quelque chose à l'indice 3 : "sffsfshfsd" . L'itération continue ensuite, en prenant la valeur de l'indice suivant (3), qui est ajoutée au début à nouveau, déplaçant la même valeur qui était à l'indice 3 à l'indice 4... Le cycle ne se termine jamais.

Dans votre deuxième boucle w in list[:] vous créez un copie de la liste entière ( en utilisant un opérateur de tranche ) et itérer à travers cela. Vous ajoutez des éléments à la liste originale, pas à la copie, donc l'itérateur ne touchera pas les éléments que vous avez ajoutés.

PS : J'ai essayé de chercher dans le code source de Python (qui est en C) pour prouver que les itérateurs de liste utilisent en fait un index incrémentiel (comme décrit ci-dessus). Je ne suis pas très doué pour la lecture du code source de Python, mais voici ce que j'ai trouvé en cpython/listobject.c :

Création d'un itérateur, met l'index de départ à 0

2797 static PyObject *
2798 list_iter(PyObject *seq)
2799 {
....
2806     it = PyObject_GC_New(listiterobject, &PyListIter_Type);
....
2809     it->it_index = 0;
....
2813     return (PyObject *)it;
2814 }

_next utilise it->it_index d'en haut et l'incrémente ensuite_

2831 static PyObject *
2832 listiter_next(listiterobject *it)
2833 {
....
2844         item = PyList_GET_ITEM(seq, it->it_index);
2845         ++it->it_index;
....
2847         return item;
....
2853 }

Ça me semble légitime ?

4voto

NPE Points 169956

Pour simuler le fonctionnement interne de l'itération de listes, réécrivons votre programme en utilisant des indices entiers et un fichier de type while boucle.

lst = ["Mohit", "kumar", "sffsfshfsd"]
pos = 0
while pos < len(lst):
  word = lst[pos]
  print('lst=%s pos=%d word=%s' % (lst, pos, word))
  if len(word) > 5:
    lst.insert(0, word)
  pos += 1

Ce qui suit montre ce qui se passe lorsque vous exécutez cette opération :

lst=['Mohit', 'kumar', 'sffsfshfsd'] pos=0 word=Mohit
lst=['Mohit', 'kumar', 'sffsfshfsd'] pos=1 word=kumar
lst=['Mohit', 'kumar', 'sffsfshfsd'] pos=2 word=sffsfshfsd
lst=['sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd'] pos=3 word=sffsfshfsd
lst=['sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd'] pos=4 word=sffsfshfsd
lst=['sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd'] pos=5 word=sffsfshfsd
lst=['sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd'] pos=6 word=sffsfshfsd
lst=['sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd'] pos=7 word=sffsfshfsd
lst=['sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd'] pos=8 word=sffsfshfsd
lst=['sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd'] pos=9 word=sffsfshfsd
...

(Cela continue jusqu'à ce que vous soyez à court de RAM ou de patience).

Comme vous pouvez le voir, vous continuez à décaler la finale 'sffsfshfsd' à droite, de sorte que votre code continue à le regarder et ne s'arrête jamais.

Cela ne se produit pas si vous travaillez sur une copie puisque vous ne modifiez plus la liste sur laquelle vous itérez.

Cela ne se produirait pas non plus si vous deviez ajuster l'index de la boucle après l'insertion :

  if len(word) > 5:
    lst.insert(0, word)
    pos += 1  # account for the extra word
  pos += 1

ou déplacer le mot au lieu de le copier :

  if len(word) > 5:
    lst.insert(0, lst.pop(pos))  # don't change len(lst)

2voto

bakatrouble Points 1240

Cela se produit parce que vous ajoutez "sffsfshfsd" à la liste à chaque itération à partir de la troisième, de sorte que la liste ne se termine jamais.

2voto

user2341726 Points 189

Dans le premier code, vous insérez des éléments dans la même liste que celle dans laquelle vous effectuez la boucle. C'est pourquoi cela continue à la boucle interne, parce que la liste augmente indéfiniment. Dans le second code, vous faites une copie, en séparant votre boucle for et votre liste d'origine, de sorte que cela finira par s'arrêter.

2voto

Ashwini Chaudhary Points 94431

Citation : à partir de la documentation :

Note : Il y a une subtilité lorsque la séquence est modifiée par la (cela ne peut se produire que pour les séquences mutables, c'est-à-dire les listes). Un compteur interne compteur interne est utilisé pour garder et il est incrémenté à chaque itération. Lorsque ce compteur a atteint la longueur de la séquence, la boucle se termine. Cela signifie que si la suite supprime l'élément actuel (ou un élément précédent) de la séquence, l'élément l'élément suivant sera ignoré (puisqu'il obtient l'index de l'élément actuel qui a déjà été traité). qui a déjà été traité). De même, si la suite insère un dans la séquence avant l'élément actuel, l'élément actuel sera l'élément actuel sera traité à nouveau lors du prochain passage dans la boucle. [ ] bogues qui peuvent être évités en faisant une copie temporaire en utilisant une tranche de la séquence entière, par exemple,

for x in a[:]:
    if x < 0: a.remove(x)

Un for-loop sur une liste en Python maintient un compteur en interne et celui-ci est utilisé pour obtenir l'élément suivant.

Dans votre premier code, lorsqu'il atteint sffsfshfsd (c'est-à-dire l'indice 2), vous l'insérez à nouveau au début de la liste, ce qui fait que tous les éléments se déplacent d'une place et maintenant sffsfshfsd sera déplacé vers l'indice 3 et sera repris lors de l'itération suivante. Et ainsi de suite...

Dans votre deuxième code, vous itérez sur une copie de liste et une copie de la liste n'est pas modifiée lorsque vous modifiez la liste originale.

lst = ["Mohit","kumar","sffsfshfsd"]
for i, w in enumerate(lst):
    print("Index: {i} | List: {list}".format(i=i, list=lst))
    if(len(w)) > 5:
        lst.insert(0, w)

Sorties :

Index: 0 | List: ['Mohit', 'kumar', 'sffsfshfsd']
Index: 1 | List: ['Mohit', 'kumar', 'sffsfshfsd']
Index: 2 | List: ['Mohit', 'kumar', 'sffsfshfsd']
Index: 3 | List: ['sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd']
Index: 4 | List: ['sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd']
Index: 5 | List: ['sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd']
Index: 6 | List: ['sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'sffsfshfsd', 'Mohit', 'kumar', 'sffsfshfsd']

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