2010-02-09 6 views
1

J'ai perdu un peu de temps dans ce Python pour la déclaration:Comment supprimer des éléments de la liste en vélo la liste elle-même sans dupliquer

class MyListContainer: 
    def __init__(self): 
     self.list = [] 

    def purge(self): 
     for object in self.list: 
      if (object.my_cond()): 
       self.list.remove(object) 
     return self.list 

container = MyListContainer() 

# now suppose both obj.my_cond() return True 
obj1 = MyCustomObject(par) 
obj2 = MyCustomObject(other_par) 

container.list = [obj1, obj2] 

# returning not an empty list but [obj2] 
container.purge() 

Il ne fonctionne pas comme je m'y attendais parce que quand le cycle " purge "supprimer le premier objet dans la liste le second est déplacé au début de la liste et le cycle est terminé.

Je résolus de dupliquer self.list avant pour le cycle:

... 
local_list = self.list[:] 
for object in local_list: 
... 

Je suppose que le travail pour arrêter déclaration parce que je change la longueur de la liste initiale. Quelqu'un peut-il clarifier ce point?

Y a-t-il un moyen plus "élégant" de résoudre ce problème? Si j'ai plus de quelques éléments dans la liste, la dupliquer à chaque fois ne semble pas être une bonne idée.

Peut-être que la fonction filter() est la bonne, mais je souhaite avoir une autre approche, le cas échéant.

Je suis un débutant.


Pour résumer vos réponses utiles:

  • Ne jamais modifier une liste que vous Looping
  • Dupliquer la liste ou la liste d'utilisation compréhensions
  • Duplication une liste ne pouvait pas perdre votre mémoire ou dans ce cas qui s'en soucie

Répondre

2

Filtre (ou compréhension de la liste) EST le chemin à parcourir.Si vous voulez le faire inplace, quelque chose comme ça fonctionnerait:

purge = [] 
for i,object in enumerate(self.list): 
    if object.mycond() 
     purge.append(i) 
for i in reversed(purge): 
    del self.list[i] 

Ou bien, la liste de purge peut être faite avec une compréhension, une version raccourci ressemble:

for i in reversed([ i for (i,o) in enumerate(self.list) if o.mycond() ]): 
    del self.list[i] 
+0

Je me demande pourquoi la modification * en place * aurait de l'importance. – SilentGhost

+0

Je préfère la première approche parce que je peux utiliser une clause try à l'intérieur de la boucle for pour être sûr de terminer la boucle. mycond() pourrait générer une erreur. – yuri

+0

La modification en place peut être importante dans les langages de bas niveau, pour des raisons de performance et de mémoire. Mais ce n'est généralement pas pertinent en Python - le surcoût de Python lui-même l'écrase. –

4

N'essayez pas. Ne fais pas ça. Faites une copie ou générez une nouvelle liste.

+0

Ok je te le promets. Mais pouvez-vous expliquer pourquoi? – yuri

+0

La modification d'une liste sur laquelle vous travaillez perturbe les structures internes requises par Python pour garder une trace de l'emplacement de l'itération. –

3

Il suffit de vous faire une nouvelle liste:

def purge(self): 
    self.list = [object for object in self.list if not object.my_cond()] 
    return self.list 

Réserve toute optimisation jusqu'à ce que vous avez trouvé et PROFILES que cette méthode est vraiment le goulot d'étranglement de votre application. (Je parie que ce ne sera pas le cas.)

+0

'sinon object.my_cond()' – SilentGhost

+0

@SilentGhost, merci! Je ne faisais pas attention. –

0

Votre deuxième solution dans laquelle vous dupliquez la liste est la bonne façon de procéder. Ensuite, vous pouvez simplement remplacer l'ancienne liste par la copie si nécessaire.

2

Dans les variables python sont en réalité des étiquettes aux données. La duplication d'une liste consiste, pour la plupart, à créer un nouvel ensemble de pointeurs sur les données de la première liste. Ne te sens pas trop mal à ce sujet.

La compréhension des listes est votre ami.

par exemple.

>>> a = range(20) 
>>> a 
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] 
>>> [ x for x in a if x % 2 == 0 ] 
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18] 
0

Il est parfaitement sûr pour raccourcir la liste en place si vous le faites en sens inverse!

>>> a=range(20) 
>>> for i in reversed(range(len(a))): 
...  if a[i]%2: del a[i] 
... 
>>> a 
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18] 

Une autre façon est de réaffecter toute tranche

>>> a=range(20) 
>>> a[:]=(x for x in a if not x%2) 
>>> a 
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18] 

Si les éléments de la liste sont uniques, cela fonctionne aussi

>>> a=range(20) 
>>> for item in reversed(a): 
... if item%2: a.remove(item) 
... 
>>> a 
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18] 

Voici quelques explications supplémentaires en réponse à des années yuri commenter

Supposons que nous ayons

>>> a=[0,1,2,3,4,5] 

maintenant essayer naïvement supprimer les 3e et articles 4e

>>> del a[3] 
>>> del a[4] 
>>> a 
[0, 1, 2, 4] # didn't work because the position of all the item with index >=3 was changed 

Cependant, si nous faisons les del « s dans l'ordre inverse

>>> a=[0,1,2,3,4,5] 
>>> del a[4] 
>>> del a[3] 
>>> a 
[0, 1, 2, 5] # this is the desired result 

étendre Maintenant cette idée sur une boucle avec une condition de suppression, et vous voyez que la suppression de la liste en direct est possible

+0

Je suppose que le premier et le troisième exemple fonctionnent comme prévu car reverse() duplique la liste en interne. Donc vous ne raccourcissez pas directement l'original – yuri

+0

@yuri, inversé sur une liste ne copie pas la liste, il renvoie un objet listreverseiterator. Vous êtes en fait en train de raccourcir la liste au fur et à mesure que vous la parcourez, mais puisque vous commencez à supprimer à la fin de la liste, l'index pointe toujours vers la partie stable de la liste –

+0

@yuri, j'ai essayé d'ajouter quelques explications. En supposant que vos éléments de liste sont uniques, changer votre code à 'pour object dans reverse (self.list):' fonctionnera bien - et ne fait pas de copie dans la liste –

-1
indeces = [] 
minus = 0 

for i in range(self.list): 
    if cond(self.list[i]): 
     indeces.append(i) 

for i in indeces: 
    self.list = self.list[:(i-minus)].extend(self.list[i-minus+1:]) 
Questions connexes