2010-06-03 3 views
4

Je voulais savoir s'il est sûr (comportement documenté?) De supprimer l'espace de domaine d'un itérateur en exécution en Python.Est-il sûr (comportement documenté?) De supprimer le domaine d'un itérateur en cours d'exécution

Tenir compte du code:

import os 
import sys 

sampleSpace = [ x*x for x in range(7) ] 

print sampleSpace 

for dx in sampleSpace: 

    print str(dx) 

    if dx == 1: 

     del sampleSpace[ 1 ] 
     del sampleSpace[ 3 ] 

    elif dx == 25: 

     del sampleSpace[ -1 ] 

print sampleSpace 

« sampleSpace » est ce que j'appelle « l'espace de domaine d'un itérateur » (s'il y a un mot plus approprié/phrase, laissez-moi savoir). Ce que je fais est de supprimer des valeurs pendant que l'itérateur 'dx' le parcourt.

Voici ce que je pense du code:

Iteration versus element being pointed to (*): 

0: [*0, 1, 4, 9, 16, 25, 36] 
1: [0, *1, 4, 9, 16, 25, 36] (delete 2nd and 5th element after this iteration) 
2: [0, 4, *9, 25, 36] 
3: [0, 4, 9, *25, 36] (delete -1th element after this iteration) 
4: [0, 4, 9, 25*] (as the iterator points to nothing/end of list, the loop terminates) 

.. et voici ce que je reçois:

[0, 1, 4, 9, 16, 25, 36] 
0 
1 
9 
25 
[0, 4, 9, 25] 

Comme vous pouvez le voir - ce que je pense est ce que je reçois - est contraire au comportement que j'ai eu d'autres langues dans un tel scénario. Par conséquent - Je voulais vous demander s'il existe une règle comme "l'itérateur devient invalide si vous mutez son espace pendant l'itération" en Python?

Est-ce sûr (comportement documenté?) En Python de faire des choses comme ça?

Répondre

6

De the Python tutorial:

Il n'est pas sûr de modifier la séquence itéré dans la boucle (ce ne peut se produire pour la séquence mutable types, comme les listes). Si vous devez modifier la liste que vous itérez sur (par exemple, pour dupliquer les éléments sélectionnés), vous devez itérer sur une copie. La notation de découpage rend cette particulièrement pratique:

>>> for x in a[:]: # make a slice copy of the entire list 
... if len(x) > 6: a.insert(0, x) 
... 
>>> a 
['defenestrate', 'cat', 'window', 'defenestrate'] 
+0

Aha! C'est ce que je cherchais. Après avoir lu ce texte, je dirais, dans mon sens du mot, que l'opération est * pas sûre * – PoorLuzer

+0

Marquer la vôtre comme la réponse car elle a directement répondu à ma question. SilentGhost est plus détaillé et riche en exemples. – PoorLuzer

+0

Parce que le comportement n'est pas défini par le langage Python, le résultat est libre de changer entre différentes implémentations, ou même des versions différentes de la même implémentation –

0

Qu'entendez-vous par sécurité? Votre code arrive de ne pas soulever des erreurs, mais il est une possibilité bien sûr, considérez ceci:

>>> a = range(3) 
>>> for i in a: 
    del a 


Traceback (most recent call last): 
    File "<pyshell#13>", line 2, in <module> 
    del a 
NameError: name 'a' is not defined 
>>> a 
[0, 1, 2] 
>>> for i in a: 
    del a[i+1] 


Traceback (most recent call last): 
    File "<pyshell#27>", line 2, in <module> 
    del a[i+1] 
IndexError: list assignment index out of range 

On ne sait pas pourquoi voudriez-vous faire cela, mais il n'y a pas de règles supplémentaires applicables à itérateurs. Ils agissent exactement comme n'importe quel autre type.

+0

je aurais besoin de fonctionner sur une liste de fichiers dans un répertoire. Maintenant, il peut arriver que mes opérations, en tant qu'effets secondaires, finissent par fonctionner sur d'autres fichiers que celui explicitement passé. Dans ce cas, je ne veux pas opérer de nouveau sur les mêmes fichiers juste parce qu'ils sont dans la même liste. Dois-je soulever cette question comme une question distincte si elle est trop floue? – PoorLuzer

+0

@PoorL: pourquoi muteriez-vous l'itérateur en premier lieu? – SilentGhost

+0

Voici ma situation: Dites que je trouve 3 fichiers texte dans le répertoire courant a, b, c, d. Je veux faire des sauvegardes de tous les fichiers texte. Par conséquent, je stocke ces noms de fichiers dans la «liste principale». Dans ma fonction qui gère la sauvegarde, je me rends compte que b et c sont en réalité des copies d'un - donc je n'ai vraiment pas besoin de les sauvegarder. Au lieu d'ajouter du code pour ignorer explicitement ces fichiers, je voudrais supprimer les entrées de la 'liste principale'. Cela amène le scénario que j'ai posté. Sinon, comment puis-je gérer cela sans ajouter de vérifications explicites pour ignorer les entrées? – PoorLuzer

1

En général non, ce n'est pas sûr et vous pouvez obtenir un comportement imprévisible. Les itérateurs ne sont pas tenus de se comporter de manière spécifique dans ces circonstances.

Qu'est-ce qui se passe dans votre exemple est

# list is [0, 1, 4, 9, 16, 25, 36] 

if dx == 1: 
    # we're at index 1 when this is true 
    del sampleSpace[ 1 ] 
    # we've removed the item at index 1, and the iterator will move to the next valid position - still index 1, but in a mutated list. We got lucky in this case 
    # the list now contains [0, 4, 9, 16, 25, 36] 
    del sampleSpace[ 3 ] 
    # we remove the item at index 3 which is (now) value 16 
    # the list now contains [0, 4, 9, 25, 36] 
elif dx == 25: 

    del sampleSpace[ -1 ] 
    # we remove the final item, list now looks like 
    # the list now contains [0, 4, 9, 25] 
Questions connexes