55 votes

Élégante façon de supprimer des éléments de la séquence en Python?

Quand je suis à l'écriture de code en Python, j'ai souvent besoin de supprimer des éléments à partir d'une liste ou d'un autre type de séquence en fonction de certains critères. Je n'ai pas trouvé une solution élégante et efficace, que de supprimer des éléments à partir d'une liste que vous sont actuellement à itérer est mauvais. Par exemple, vous ne pouvez pas faire ceci:

for name in names:
    if name[-5:] == 'Smith':
        names.remove(name)

J'ai l'habitude de faire quelque chose comme ceci:

toremove = []
for name in names:
    if name[-5:] == 'Smith':
        toremove.append(name)
for name in toremove:
    names.remove(name)
del toremove

C'est innefficient, assez laid et éventuellement buggy (comment fait-il gérer plusieurs "John Smith" entrées?). Quelqu'un aurait-il une solution plus élégante, ou au moins plus efficace?

Que diriez-vous celui qui fonctionne avec les dictionnaires?

56voto

John Points 5478

Deux façons de le faire juste le filtrage sont:

  1. À l'aide de filter:

    names = filter(lambda name: name[-5:] != "Smith", names)

  2. En utilisant des interprétations de la liste:

    names = [name for name in names if name[-5:] != "Smith"]

Notez que dans les deux cas garder les valeurs pour lesquelles la fonction de prédicat évalue True, de sorte que vous devez inverser la logique (c'est à dire vous dire "garder les gens qui n'ont pas le nom de famille Smith" au lieu de "supprimer les gens qui ont le nom de famille Smith").

Edit Drôle... deux personnes individuellement affichées à la fois des réponses que j'ai proposé comme j'ai été poster le mien.

37voto

Vous pouvez également faire une itération vers l'arrière sur la liste:

for name in reversed(names):
    if name[-5:] == 'Smith':
        names.remove(name)

Ceci a l'avantage de ne pas créer une nouvelle liste (comme filter ou une compréhension de liste) et utilise un itérateur au lieu d'une liste de copie (comme [:]).

Notez que bien que la suppression d'éléments lors de l'itération en arrière est sûr, en les insérant est un peu plus compliqué.

28voto

Edward Loper Points 5936

La réponse la plus évidente est celle que Jean et quelques autres personnes ont donné, à savoir:

>>> names = [name for name in names if name[-5:] != "Smith"]       # <-- slower

Mais qui a l'inconvénient qu'il crée une nouvelle liste d'objet, plutôt que de la réutilisation de l'objet d'origine. J'ai fait un peu de profilage et d'expérimentation, et la méthode la plus efficace, je suis venu avec:

>>> names[:] = (name for name in names if name[-5:] != "Smith")    # <-- faster

L'attribution de noms "[:]" signifie "remplacer le contenu de la liste de noms avec la valeur suivante". C'est d'affectation de noms, qu'il n'est pas de créer une nouvelle liste d'objet. La partie droite de l'affectation est un générateur d'expression (notez l'utilisation de parenthèses, plutôt qu'entre crochets). Ce sera la cause de Python pour itérer à travers la liste.

Certains rapide de profilage suggère que c'est environ 30% plus rapide que la liste de compréhension de la démarche, et d'environ 40% plus rapide que le filtre approche.

Mise en garde: bien que cette solution est plus rapide que la solution la plus évidente, c'est plus obscur, et s'appuie sur plus avancées Python techniques. Si vous ne l'utilisez, je vous recommande l'accompagnant d'un commentaire. C'est probablement seulement en valeur de l'aide dans le cas où vous vous souciez vraiment de la performance de cette opération particulière (ce qui est assez rapide n'importe quoi). (Dans le cas où je l'ai utilisé, j'ai été faire Un* faisceau de recherche, et utilisé cette option pour supprimer les points de recherche de la recherche de la poutre.)

10voto

Corey Points 5286

À l'aide d'une compréhension de liste

list = [x for x in list if x[-5:] != "smith"]

4voto

gooli Points 2132

Il ya des moments où le filtrage (soit en utilisant un filtre ou d'une compréhension de liste) ne fonctionne pas. Ce qui se passe quand un autre objet est la détention d'une référence à la liste que vous modifiez et vous avez besoin de modifier la liste à la place.

for name in names[:]:
    if name[-5:] == 'Smith':
        names.remove(name)

La seule différence à partir du code original est l'utilisation de l' names[:] au lieu de names dans la boucle for. De cette façon, le code effectue une itération sur une (profonde) copie de la liste et les absorptions de fonctionner comme prévu. Depuis la liste de copie est peu profonde, il est assez rapide.

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