Je sais qu'il n'est pas autorisé de supprimer des éléments lors de l'itération d'une liste, mais est-il autorisé d'ajouter des éléments à une liste python pendant l'itération. Voici un exemple:
for a in myarr:
if somecond(a):
myarr.append(newObj())
J'ai essayé cela dans mon code et cela semble bien fonctionner, mais je ne sais pas si c'est parce que j'ai juste de la chance et que ça va casser à un moment donné dans le futur?
EDIT: Je préfère ne pas copier la liste car "myarr" est énorme, et donc ce serait trop lent. J'ai également besoin de vérifier les objets ajoutés avec "somecond ()".
EDIT: À un moment donné, "somecond (a)" sera faux, il ne peut donc pas y avoir de boucle infinie.
EDIT: Quelqu'un a posé des questions sur la fonction "somecond ()". Chaque objet dans myarr a une taille, et chaque fois que "une seconde (a)" est vrai et qu'un nouvel objet est ajouté à la liste, le nouvel objet aura une taille inférieure à a. "somecond ()" a un epsilon pour savoir comment les petits objets peuvent être et s'ils sont trop petits, il retournera "false"
Vous pouvez utiliser le islice
d'itertools pour créer un itérateur sur une plus petite partie de la liste. Ensuite, vous pouvez ajouter des entrées à la liste sans affecter les éléments sur lesquels vous répétez:
islice( myarr, 0, len(myarr)-1 )
Encore mieux, vous n'avez même pas à répéter tous les éléments. Vous pouvez incrémenter une taille de pas.
Pourquoi ne le faites-vous pas simplement de la manière idiomatique C? Cela devrait être à l'épreuve des balles, mais ce ne sera pas rapide. Je suis assez sûr d'indexer dans une liste dans Python parcourt la liste liée, donc c'est un algorithme "Shlemiel the Painter". Mais je n'ai pas tendance à m'inquiéter de l'optimisation jusqu'à ce qu'il devienne clair qu'un une section particulière du code est vraiment un problème. D'abord, faites-le fonctionner, puis souciez-vous de le rendre rapide, si nécessaire.
Si vous souhaitez parcourir tous les éléments:
i = 0
while i < len(some_list):
more_elements = do_something_with(some_list[i])
some_list.extend(more_elements)
i += 1
Si vous voulez seulement parcourir les éléments qui étaient à l'origine dans la liste:
i = 0
original_len = len(some_list)
while i < original_len:
more_elements = do_something_with(some_list[i])
some_list.extend(more_elements)
i += 1
bien, selon http://docs.python.org/tutorial/controlflow.html
Il n'est pas sûr de modifier la séquence en cours d'itération dans la boucle (cela ne peut se produire que pour les types de séquence mutables, tels que les listes). Si vous devez modifier la liste que vous parcourez (par exemple, pour dupliquer les éléments sélectionnés), vous devez itérer sur une copie.
Tu peux le faire.
bonus_rows = []
for a in myarr:
if somecond(a):
bonus_rows.append(newObj())
myarr.extend( bonus_rows )
En bref : Si vous êtes absolument sûr que tous les nouveaux objets échouent somecond()
check, alors votre code fonctionne bien, il gaspille juste certains temps itération des objets nouvellement ajoutés.
Avant de donner une bonne réponse, vous devez comprendre pourquoi il considère une mauvaise idée de modifier la liste/dict pendant l'itération. Lors de l'utilisation de l'instruction for
, Python
essaie d'être intelligent , et renvoie à chaque fois un élément calculé dynamiquement. Prenons list
comme exemple, python
se souvient d'un index et chaque fois qu'il renvoie l[index]
à toi. Si vous modifiez l
, le résultat l[index]
peut être désordonné.
[~ # ~] note [~ # ~] : Voici une stackoverflow question pour le démontrer.
Le pire des cas pour ajouter un élément pendant l'itération est boucle infinie , essayez (ou non si vous pouvez lire un bogue) ce qui suit dans un python REPL:
import random
l = [0]
for item in l:
l.append(random.randint(1, 1000))
print item
Il imprime des nombres sans arrêt jusqu'à ce que la mémoire soit épuisée ou détruite par le système/l'utilisateur.
Comprenez la raison interne, discutons des solutions. Voici quelques-uns:
Itérer la liste d'origine et modifier celle copiée.
result = l[:]
for item in l:
if somecond(item):
result.append(Obj())
Au lieu de gérer le contrôle en python, vous décidez comment itérer la liste:
length = len(l)
for index in range(length):
if somecond(l[index]):
l.append(Obj())
Avant d'itérer, calculez la longueur de la liste et ne bouclez que length
fois.
Au lieu de modifier la liste d'origine, stockez le nouvel objet dans une nouvelle liste et concaténez-les ensuite.
added = [Obj() for item in l if somecond(item)]
l.extend(added)
Accédez à vos éléments de liste directement par i. Ensuite, vous pouvez ajouter à votre liste:
for i in xrange(len(myarr)):
if somecond(a[i]):
myarr.append(newObj())
faire une copie de votre liste d'origine, itérer dessus, voir le code modifié ci-dessous
for a in myarr[:]:
if somecond(a):
myarr.append(newObj())
J'ai eu un problème similaire aujourd'hui. J'avais une liste d'articles qui devaient être vérifiés; si les objets ont réussi la vérification, ils ont été ajoutés à une liste de résultats. S'ils ne l'ont pas réussissent, je les ai un peu modifiés et s'ils peuvent toujours fonctionner (taille> 0 après le changement), je les ajouterais au dos de la liste pour revérifier.
Je suis allé pour une solution comme
items = [...what I want to check...]
result = []
while items:
recheck_items = []
for item in items:
if check(item):
result.append(item)
else:
item = change(item) # Note that this always lowers the integer size(),
# so no danger of an infinite loop
if item.size() > 0:
recheck_items.append(item)
items = recheck_items # Let the loop restart with these, if any
Ma liste est effectivement une file d'attente, aurait probablement dû utiliser une sorte de file d'attente. Mais mes listes sont petites (comme 10 articles) et cela fonctionne aussi.
Vous pouvez utiliser un index et une boucle while au lieu d'une boucle for si vous souhaitez que la boucle boucle également sur les éléments ajoutés à la liste pendant la boucle:
i = 0
while i < len(myarr):
a = myarr[i];
i = i + 1;
if somecond(a):
myarr.append(newObj())
Solution alternative sur une seule ligne:
reduce(lambda x,y : x +[newObj] if somecond else x,myarr,myarr)
Extension de la réponse de S.Lott pour que les nouveaux éléments soient également traités:
todo = myarr
done = []
while todo:
added = []
for a in todo:
if somecond(a):
added.append(newObj())
done.extend(todo)
todo = added
La liste finale est dans done
.