Jak usunąć elementy z listy podczas iteracji?

Przeglądam listę krotek w Pythonie i próbuję je usunąć, jeśli spełniają określone kryteria.

for tup in somelist:
    if determine(tup):
         code_to_remove_tup

Co powinienem użyć zamiast code_to_remove_tup? Nie mogę wymyślić, jak usunąć przedmiot w ten sposób.

20 answers

Możesz użyć rozumienia listy, aby utworzyć nową listę zawierającą tylko elementy, których nie chcesz usuwać:

somelist = [x for x in somelist if not determine(x)]

Lub, przypisując do plasterka somelist[:], możesz zmutować istniejącą listę tak, aby zawierała tylko te elementy, które chcesz:

somelist[:] = [x for x in somelist if not determine(x)]

Takie podejście może być przydatne, jeśli istnieją inne odniesienia do somelist, które muszą odzwierciedlać zmiany.

Zamiast rozumienia, można również użyć itertools. W Pythonie 2:

from itertools import ifilterfalse
somelist[:] = ifilterfalse(determine, somelist)

Lub w Pythonie 3:

from itertools import filterfalse
somelist[:] = filterfalse(determine, somelist)
 612
Author: David Raznick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-21 22:17:28

Odpowiedzi sugerujące składanie list są prawie poprawne -- z tym wyjątkiem, że budują zupełnie nową listę, a następnie nadają jej tę samą nazwę starej liście, ponieważ nie modyfikują starej listy w miejscu. To różni się od tego, co robisz poprzez selektywne usuwanie, jak w sugestii @ Lennart-jest szybsze, ale jeśli Twoja lista jest dostępna za pomocą wielu odniesień, fakt, że po prostu reseatujesz jeden z odniesień i nie zmieniasz samego obiektu listy, może prowadzić do subtelnych, katastrofalnych skutków robaki.

Na szczęście niezwykle łatwo jest uzyskać zarówno szybkość kompilacji list, jak i wymaganą semantykę zmiany w miejscu -- wystarczy kod:

somelist[:] = [tup for tup in somelist if determine(tup)]

Zwróć uwagę na subtelną różnicę w stosunku do innych odpowiedzi: ta nie jest przypisywana do nazwy barename - jest przypisywana do plasterka listy, który tak się składa, że jest całą listą, zastępując w ten sposób zawartość listy w obrębie tego samego obiektu List Pythona, a nie tylko reseating jednego odniesienia (z poprzedniego obiektu list do nowego obiektu list) podobnie jak pozostałe odpowiedzi.

 502
Author: Alex Martelli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-28 10:42:00

Musisz najpierw pobrać kopię listy i ją przetworzyć, albo iteracja zakończy się niepowodzeniem z nieoczekiwanymi rezultatami.

Na przykład (zależy od rodzaju listy):

for tup in somelist[:]:
    etc....

Przykład:

>>> somelist = range(10)
>>> for x in somelist:
...     somelist.remove(x)
>>> somelist
[1, 3, 5, 7, 9]

>>> somelist = range(10)
>>> for x in somelist[:]:
...     somelist.remove(x)
>>> somelist
[]
 203
Author: Lennart Regebro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-21 22:24:30
for i in xrange(len(somelist) - 1, -1, -1):
    if some_condition(somelist, i):
        del somelist[i]

Musisz się cofnąć, bo inaczej to trochę jak piłowanie gałęzi, na której siedzisz: -)

 81
Author: John Machin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-30 15:44:59

Najlepszym podejściem do takiego przykładu byłoby zrozumienie listy

somelist = [tup for tup in somelist if determine(tup)]

W przypadkach, gdy robisz coś bardziej skomplikowanego niż wywoływanie funkcji determine, wolę konstruować nową listę i po prostu dodawać do niej, gdy idę. Na przykład

newlist = []
for tup in somelist:
    # lots of code here, possibly setting things up for calling determine
    if determine(tup):
        newlist.append(tup)
somelist = newlist

Skopiowanie listy za pomocą remove może sprawić, że Twój kod będzie wyglądał trochę czystiej, jak opisano w jednej z poniższych odpowiedzi. Zdecydowanie nie powinieneś tego robić w przypadku bardzo dużych list, ponieważ dotyczy to najpierw kopiowanie całej listy, a także wykonywanie O(n) remove operacja dla każdego usuwanego elementu, czyniąc to algorytmem O(n^2).

for tup in somelist[:]:
    # lots of code here, possibly setting things up for calling determine
    if determine(tup):
        newlist.append(tup)
 45
Author: Eli Courtwright,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-30 17:30:54

Dla tych, którzy lubią Programowanie funkcyjne:

somelist[:] = filter(lambda tup: not determine(tup), somelist)

Lub

from itertools import ifilterfalse
somelist[:] = list(ifilterfalse(determine, somelist))
 35
Author: Cide,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-24 12:50:20

The official Python 2 tutorial 4.2. "for Statements" says :

Jeśli chcesz zmodyfikować sekwencję iteracji wewnątrz pętli (na przykład aby powielić zaznaczone elementy), zaleca się, aby najpierw wykonać kopię. Iteracja nad sekwencją nie tworzy kopii. Zapis slice sprawia, że jest to szczególnie wygodne:

>>> for w in words[:]:  # Loop over a slice copy of the entire list.
...     if len(w) > 6:
...         words.insert(0, w)
...
>>> words
['defenestrate', 'cat', 'window', 'defenestrate']

Czyli to, co sugerowano na: https://stackoverflow.com/a/1207427/895245

Dokumentacja Pythona 2 7.3. "The for statement" daje taką samą radę :

Uwaga: Istnieje subtelność, gdy sekwencja jest modyfikowana przez pętlę(może to nastąpić tylko dla sekwencji mutowalnych, tj. list). Wewnętrzny licznik jest używany do śledzenia, który element jest używany dalej, a ten jest zwiększany przy każdej iteracji. Gdy licznik ten osiągnie długość sekwencji pętla / align = "left" / Oznacza to, że jeśli pakiet usunie bieżący (lub poprzedni) element z sekwencji, następny element zostanie pominięty (ponieważ otrzyma indeks bieżącego elementu, który został już poddany obróbce). Podobnie, jeśli pakiet wstawi element w sekwencji przed bieżącym elementem, bieżący element będzie traktowany ponownie następnym razem przez pętlę. Może to prowadzić do nieprzyjemnych błędów, których można uniknąć, wykonując tymczasową kopię za pomocą kawałka całej sekwencji, np.,

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

Czy Python mógłby zrobić to lepiej?

Wydaje się, że ten konkretny API Pythona może zostać ulepszony. Porównaj go na przykład z jego odpowiednikiem w języku Java ListIterator , co sprawia, że nie można modyfikować iteracyjnej listy, z wyjątkiem samego iteratora, i daje skuteczne sposoby, aby to zrobić bez kopiowania listy. Dalej, Python!

 32
Author: Ciro Santilli 新疆改造中心 六四事件 法轮功,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-06 14:09:27

Mądrze byłoby również utworzyć nową listę, Jeśli bieżąca pozycja listy spełnia pożądane kryteria.

Więc:

for item in originalList:
   if (item != badValue):
        newList.append(item)

I aby uniknąć konieczności ponownego kodowania całego projektu z nową nazwą list:

originalList[:] = newList

Uwaga, z Dokumentacji Pythona:

Zrozumiałem.copy (x) Zwraca płytką kopię x. Zrozumiałem.deepcopy (x) Zwraca głęboką kopię x.
 9
Author: ntk4,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-23 02:33:13

Musiałem to zrobić z ogromną listą, a powielanie listy wydawało się kosztowne, tym bardziej, że w moim przypadku liczba usunięć byłaby niewielka w porównaniu do pozycji, które pozostały. Przyjęłam takie podejście na niskim poziomie.

array = [lots of stuff]
arraySize = len(array)
i = 0
while i < arraySize:
    if someTest(array[i]):
        del array[i]
        arraySize -= 1
    else:
        i += 1

To, czego Nie wiem, to jak wydajne są kilka usunięć w porównaniu do kopiowania dużej listy. Proszę skomentować, jeśli masz wgląd.

 8
Author: Michael,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-13 20:54:41

Ta odpowiedź została pierwotnie napisana w odpowiedzi na pytanie, które od tego czasu zostało oznaczone jako DUPLIKAT: usuwanie współrzędnych z listy w Pythonie

W Twoim kodzie są dwa problemy:

1) Podczas używania metody remove(), próbujesz usunąć liczby całkowite, podczas gdy musisz usunąć krotkę.

2) pętla for pomija pozycje na liście.

Sprawdźmy, co się stanie, gdy wykonamy Twój kod:

>>> L1 = [(1,2), (5,6), (-1,-2), (1,-2)]
>>> for (a,b) in L1:
...   if a < 0 or b < 0:
...     L1.remove(a,b)
... 
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
TypeError: remove() takes exactly one argument (2 given)

Pierwszy problem polega na tym, że ty Przekazuje zarówno 'a', jak i 'b' do remove (), ale remove () akceptuje tylko jeden argument. Jak więc możemy sprawić, by funkcja remove() działała poprawnie z Twoją listą? Musimy dowiedzieć się, jaki jest każdy element Twojej listy. W tym przypadku każda z nich jest krotką. Aby to zobaczyć, uzyskajmy dostęp do jednego elementu listy (indeksowanie zaczyna się od 0):

>>> L1[1]
(5, 6)
>>> type(L1[1])
<type 'tuple'>

Aha! Każdy element L1 jest w rzeczywistości krotką. To jest to, co musimy przekazać, aby usunąć (). Krotki w Pythonie są bardzo proste, są po prostu wykonane przez załączenie wartości w nawiasach. "a, b" nie jest krotką, ale "(a, b)" jest krotką. Więc zmodyfikujemy Twój kod i uruchomimy go ponownie:

# The remove line now includes an extra "()" to make a tuple out of "a,b"
L1.remove((a,b))

Ten kod działa bez żadnego błędu, ale spójrzmy na listę, którą wyświetla:

L1 is now: [(1, 2), (5, 6), (1, -2)]

Dlaczego (1,-2) jest jeszcze na twojej liście? Okazuje się, że modyfikowanie listy przy użyciu pętli do iteracji jest bardzo złym pomysłem bez specjalnej troski. Powodem, dla którego (1, -2) pozostaje na liście jest to, że lokalizacje każdego elementu w liście zmieniały się między iteracjami pętla for. Przyjrzyjmy się, co się stanie, jeśli podamy powyższy kod dłuższej liście:

L1 = [(1,2),(5,6),(-1,-2),(1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
### Outputs:
L1 is now: [(1, 2), (5, 6), (1, -2), (3, 4), (5, 7), (2, 1), (5, -1), (0, 6)]

Jak można wywnioskować z tego wyniku, za każdym razem, gdy instrukcja warunkowa zostanie obliczona na true, a element listy zostanie usunięty, następna iteracja pętli pominie ewaluację następnego elementu na liście, ponieważ jego wartości znajdują się teraz w różnych indeksach.

Najbardziej intuicyjnym rozwiązaniem jest skopiowanie listy, następnie iteracja nad oryginalną listą i tylko modyfikacja kopii. Możesz spróbować robiąc tak:

L2 = L1
for (a,b) in L1:
    if a < 0 or b < 0 :
        L2.remove((a,b))
# Now, remove the original copy of L1 and replace with L2
print L2 is L1
del L1
L1 = L2; del L2
print ("L1 is now: ", L1)

Jednak wynik będzie identyczny jak przed:

'L1 is now: ', [(1, 2), (5, 6), (1, -2), (3, 4), (5, 7), (2, 1), (5, -1), (0, 6)]

Dzieje się tak dlatego, że kiedy tworzyliśmy L2, python nie stworzył nowego obiektu. Zamiast tego odwołuje się tylko do L2 do tego samego obiektu co L1. Możemy to zweryfikować za pomocą "is", które różni się od "equals" (= = ).

>>> L2=L1
>>> L1 is L2
True

Możemy zrobić prawdziwą kopię używając copy.copy (). Wtedy wszystko działa zgodnie z oczekiwaniami: {]}

import copy
L1 = [(1,2), (5,6),(-1,-2), (1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
L2 = copy.copy(L1)
for (a,b) in L1:
    if a < 0 or b < 0 :
        L2.remove((a,b))
# Now, remove the original copy of L1 and replace with L2
del L1
L1 = L2; del L2
>>> L1 is now: [(1, 2), (5, 6), (3, 4), (5, 7), (2, 1), (0, 6)]

W końcu jest jedno czystsze rozwiązanie niż konieczność zrób zupełnie nową kopię L1. Funkcja odwrócona ():

L1 = [(1,2), (5,6),(-1,-2), (1,-2),(3,4),(5,7),(-4,4),(2,1),(-3,-3),(5,-1),(0,6)]
for (a,b) in reversed(L1):
    if a < 0 or b < 0 :
        L1.remove((a,b))
print ("L1 is now: ", L1)
>>> L1 is now: [(1, 2), (5, 6), (3, 4), (5, 7), (2, 1), (0, 6)]

Niestety, nie potrafię dokładnie opisać, jak działa funkcja odwrócona (). Zwraca obiekt 'listreverseiterator' po przekazaniu do niego listy. Dla praktycznych celów można myśleć o tym jako o stworzeniu odwróconej kopii jego argumentu. Takie rozwiązanie polecam.

 7
Author: Cinghiale,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:24

Jeśli chcesz zrobić cokolwiek innego podczas iteracji, może być miło uzyskać zarówno indeks (który gwarantuje Ci możliwość odniesienia się do niego, na przykład jeśli masz listę dictów), jak i rzeczywistą zawartość elementu listy.

inlist = [{'field1':10, 'field2':20}, {'field1':30, 'field2':15}]    
for idx, i in enumerate(inlist):
    do some stuff with i['field1']
    if somecondition:
        xlist.append(idx)
for i in reversed(xlist): del inlist[i]

enumerate daje dostęp do artykułu i indeksu jednocześnie. reversed jest tak, aby indeksy, które zamierzasz później usunąć, nie zmieniły się na Tobie.

 4
Author: fantabolous,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-18 12:30:16

Możesz użyć filter() dostępnego jako wbudowany.

Aby uzyskać więcej informacji Sprawdź tutaj

 4
Author: Xolve,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-27 07:40:53

Możesz spróbować for-looping w odwrotnej kolejności, więc dla some_list zrobisz coś w stylu:

list_len = len(some_list)
for i in range(list_len):
    reverse_i = list_len - 1 - i
    cur = some_list[reverse_i]

    # some logic with cur element

    if some_condition:
        some_list.pop(reverse_i)

W ten sposób indeks jest wyrównany i nie cierpi z powodu aktualizacji listy (niezależnie od tego, czy pop cur element, czy nie).

 3
Author: Queequeg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-10 20:58:49

Jedno możliwe rozwiązanie, przydatne, jeśli chcesz nie tylko usunąć niektóre rzeczy, ale także zrobić coś ze wszystkimi elementami w jednej pętli:

alist = ['good', 'bad', 'good', 'bad', 'good']
i = 0
for x in alist[:]:
    if x == 'bad':
        alist.pop(i)
        i -= 1
    # do something cool with x or just print x
    print(x)
    i += 1
 3
Author: Alexey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-13 19:38:52

Musiałem zrobić coś podobnego i w moim przypadku problemem była pamięć - musiałem połączyć wiele obiektów dataset w ramach listy, po zrobieniu kilku rzeczy z nimi, jako nowy obiekt, i musiałem pozbyć się każdego wpisu, który scalałem, aby uniknąć powielania wszystkich z nich i wysadzenia pamięci. W moim przypadku posiadanie obiektów w słowniku zamiast listy działało dobrze:

```

k = range(5)
v = ['a','b','c','d','e']
d = {key:val for key,val in zip(k, v)}

print d
for i in range(5):
    print d[i]
    d.pop(i)
print d

```

 2
Author: rafa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-16 11:05:41

TLDR:

Napisałem bibliotekę, która pozwala ci to zrobić:

from fluidIter import FluidIterable
fSomeList = FluidIterable(someList)  
for tup in fSomeList:
    if determine(tup):
        # remove 'tup' without "breaking" the iteration
        fSomeList.remove(tup)
        # tup has also been removed from 'someList'
        # as well as 'fSomeList'

Najlepiej jest użyć innej metody, jeśli to możliwe, która nie wymaga modyfikowania iterowalnego podczas iteracji, ale dla niektórych algorytmów może to nie być takie proste. A więc jeśli jesteś pewien, że naprawdę chcesz wzorca kodu opisanego w oryginalnym pytaniu, jest to możliwe.

Powinien działać na wszystkich mutowalnych sekwencjach, a nie tylko na listach.


Pełne odpowiedź:

Edit: ostatni przykład kodu w tej odpowiedzi daje przypadek użycia dla dlaczego czasami możesz chcieć zmodyfikować listę na swoim miejscu, zamiast używać jej rozumienia. Pierwsza część odpowiedzi służy jako samouczek jak tablica może być modyfikowana w miejscu.

Rozwiązanie wynika z tej odpowiedzi (na powiązane pytanie) od senderle. Co wyjaśnia jak indeks tablicy jest aktualizowany podczas iteracji poprzez lista, która została zmodyfikowana. Poniższe rozwiązanie jest przeznaczone do prawidłowego śledzenia indeksu tablicy, nawet jeśli lista jest modyfikowana.

Pobierz fluidIter.py z tutaj https://github.com/alanbacon/FluidIterator, jest to tylko jeden plik, więc nie ma potrzeby instalowania git. Nie ma instalatora, więc musisz upewnić się, że plik znajduje się w ścieżce Pythona. Kod został napisany dla Pythona 3 i nie jest testowany na Pythonie 2.

from fluidIter import FluidIterable
l = [0,1,2,3,4,5,6,7,8]  
fluidL = FluidIterable(l)                       
for i in fluidL:
    print('initial state of list on this iteration: ' + str(fluidL)) 
    print('current iteration value: ' + str(i))
    print('popped value: ' + str(fluidL.pop(2)))
    print(' ')

print('Final List Value: ' + str(l))

Spowoduje to następujące Wyjście:

initial state of list on this iteration: [0, 1, 2, 3, 4, 5, 6, 7, 8]
current iteration value: 0
popped value: 2

initial state of list on this iteration: [0, 1, 3, 4, 5, 6, 7, 8]
current iteration value: 1
popped value: 3

initial state of list on this iteration: [0, 1, 4, 5, 6, 7, 8]
current iteration value: 4
popped value: 4

initial state of list on this iteration: [0, 1, 5, 6, 7, 8]
current iteration value: 5
popped value: 5

initial state of list on this iteration: [0, 1, 6, 7, 8]
current iteration value: 6
popped value: 6

initial state of list on this iteration: [0, 1, 7, 8]
current iteration value: 7
popped value: 7

initial state of list on this iteration: [0, 1, 8]
current iteration value: 8
popped value: 8

Final List Value: [0, 1]

Powyżej mamy użyto metody pop w obiekcie fluid list. Zaimplementowane są również inne popularne metody iteracyjne, takie jak del fluidL[i], .remove, .insert, .append, .extend. Listę można również modyfikować za pomocą plasterków (metodysort i reverse nie są zaimplementowane).

Jedynym warunkiem jest to, że musisz tylko zmodyfikować listę w miejscu, jeśli w dowolnym momencie fluidL lub l zostaną przypisane do innego obiektu listy, kod nie będzie działał. Oryginalny obiekt fluidL nadal byłby używany przez pętlę for, ale stań się poza zasięgiem naszych modyfikacji.

Tzn.

fluidL[2] = 'a'   # is OK
fluidL = [0, 1, 'a', 3, 4, 5, 6, 7, 8]  # is not OK

Jeśli chcemy uzyskać dostęp do bieżącej wartości indeksu listy, nie możemy użyć enumerate, ponieważ liczy się tylko ile razy pętla for została uruchomiona. Zamiast tego użyjemy obiektu iterator bezpośrednio.

fluidArr = FluidIterable([0,1,2,3])
# get iterator first so can query the current index
fluidArrIter = fluidArr.__iter__()
for i, v in enumerate(fluidArrIter):
    print('enum: ', i)
    print('current val: ', v)
    print('current ind: ', fluidArrIter.currentIndex)
    print(fluidArr)
    fluidArr.insert(0,'a')
    print(' ')

print('Final List Value: ' + str(fluidArr))

Spowoduje to następujące Wyjście:

enum:  0
current val:  0
current ind:  0
[0, 1, 2, 3]

enum:  1
current val:  1
current ind:  2
['a', 0, 1, 2, 3]

enum:  2
current val:  2
current ind:  4
['a', 'a', 0, 1, 2, 3]

enum:  3
current val:  3
current ind:  6
['a', 'a', 'a', 0, 1, 2, 3]

Final List Value: ['a', 'a', 'a', 'a', 0, 1, 2, 3]

Klasa FluidIterable zapewnia tylko opakowanie dla oryginalnego obiektu list. Oryginalny obiekt może być dostępny jako własność obiektu płynnego, jak więc:

originalList = fluidArr.fixedIterable

Więcej przykładów / testów można znaleźć w sekcji if __name__ is "__main__": na dole fluidIter.py. Są one warte obejrzenia, ponieważ wyjaśniają, co dzieje się w różnych sytuacjach. Na przykład: Zamiana dużych sekcji listy za pomocą plasterka. Lub używanie (i modyfikowanie) tej samej iterable w zagnieżdżonych pętlach for.

Jak powiedziałem na początek: jest to skomplikowane rozwiązanie, które zaszkodzi czytelności kodu i utrudni debugowanie. Dlatego też inne rozwiązania takie jak zestawienie listy wymienione w odpowiedzi Davida Raznicka powinny być rozważone jako pierwsze. To powiedziawszy, znalazłem czasy, w których ta klasa była dla mnie przydatna i była łatwiejsza w użyciu niż śledzenie indeksów elementów, które wymagają usunięcia.


Edit: jak wspomniano w komentarzach, ta odpowiedź tak naprawdę nie przedstawia problemu, dla którego takie podejście stanowi rozwiązanie. Postaram się odnieść do tego tutaj:

Lista zawiera sposób generowania nowej listy, ale te podejścia mają tendencję do patrzenia na każdy element w izolacji, a nie na aktualny stan listy jako całości.

Tzn.

newList = [i for i in oldList if testFunc(i)]

Ale co jeśli wynik testFunc zależy od elementów, które zostały już dodane do newList? Czy elementy wciąż w oldList, które mogą być dodane obok? Może być jeszcze sposób na użycie listy, ale zacznie ona tracić swoją elegancję, a dla mnie łatwiej jest zmodyfikować listę w miejsce.

Poniższy kod jest jednym z przykładów algorytmu, który cierpi na powyższy problem. Algorytm zmniejszy listę tak, aby żaden element nie był wielokrotnością żadnego innego elementu.

randInts = [70, 20, 61, 80, 54, 18, 7, 18, 55, 9]
fRandInts = FluidIterable(randInts)
fRandIntsIter = fRandInts.__iter__()
# for each value in the list (outer loop)
# test against every other value in the list (inner loop)
for i in fRandIntsIter:
    print(' ')
    print('outer val: ', i)
    innerIntsIter = fRandInts.__iter__()
    for j in innerIntsIter:
        innerIndex = innerIntsIter.currentIndex
        # skip the element that the outloop is currently on
        # because we don't want to test a value against itself
        if not innerIndex == fRandIntsIter.currentIndex:
            # if the test element, j, is a multiple 
            # of the reference element, i, then remove 'j'
            if j%i == 0:
                print('remove val: ', j)
                # remove element in place, without breaking the
                # iteration of either loop
                del fRandInts[innerIndex]
            # end if multiple, then remove
        # end if not the same value as outer loop
    # end inner loop
# end outerloop

print('')
print('final list: ', randInts)

Wyniki i ostateczna Skrócona lista są pokazane poniżej

outer val:  70

outer val:  20
remove val:  80

outer val:  61

outer val:  54

outer val:  18
remove val:  54
remove val:  18

outer val:  7
remove val:  70

outer val:  55

outer val:  9
remove val:  18

final list:  [20, 61, 7, 55, 9]
 2
Author: Resonance,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:24

Inne odpowiedzi są poprawne, że zazwyczaj jest złym pomysłem, aby usunąć z listy, która jest iteracją. Odwrotna iteracja pozwala uniknąć pułapek, ale o wiele trudniej jest podążać za kodem, który to robi, więc zazwyczaj lepiej jest używać rozumienia listy lub filter.

Jest jednak jeden przypadek, w którym bezpieczne jest usuwanie elementów z sekwencji, którą iteracyjnie wykonujesz: jeśli usuwasz tylko jeden element podczas iteracji. Można to zapewnić za pomocą return lub break. Na przykład:

for i, item in enumerate(lst):
    if item % 4 == 0:
        foo(item)
        del lst[i]
        break

Jest to często łatwiejsze do zrozumienia niż zrozumienie listy, gdy wykonujesz pewne operacje z efektami ubocznymi na pierwszym elemencie listy, który spełnia pewien warunek, a następnie usuwasz ten element z listy natychmiast po tym.

 1
Author: Beefster,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-15 23:42:50

Do wszystkiego, co ma potencjał, aby być naprawdę duże, używam następujących.

import numpy as np

orig_list = np.array([1, 2, 3, 4, 5, 100, 8, 13])

remove_me = [100, 1]

cleaned = np.delete(orig_list, remove_me)
print(cleaned)
To powinno być znacznie szybsze niż cokolwiek innego.
 0
Author: CENTURION,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-22 23:36:31

W niektórych sytuacjach, gdy robisz coś więcej niż tylko filtrowanie listy po jednym elemencie naraz, chcesz, aby Twoja iteracja uległa zmianie podczas iteracji.

Oto przykład, w którym wcześniejsze skopiowanie listy jest nieprawidłowe, odwrotna iteracja jest niemożliwa, a zrozumienie listy również nie jest opcją.

""" Sieve of Eratosthenes """

def generate_primes(n):
    """ Generates all primes less than n. """
    primes = list(range(2,n))
    idx = 0
    while idx < len(primes):
        p = primes[idx]
        for multiple in range(p+p, n, p):
            try:
                primes.remove(multiple)
            except ValueError:
                pass #EAFP
        idx += 1
        yield p
 0
Author: CodeKid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-21 16:14:52

Od razu chcesz utworzyć kopię listy, aby mieć ją jako odniesienie podczas iteracji i usuwania krotek z tej listy, które spełniają określone kryteria.

Wtedy to zależy od tego, jakiego typu listy chcesz dla wyjścia, czy będzie to lista usuniętych krotek, czy lista krotek, które nie są usuwane.

Jak zauważył David, zalecam zrozumienie listy, aby zachować elementy, których nie chcesz usunąć.

somelist = [x for x in somelist if not determine(x)]
 -1
Author: spacexengineer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-01 05:42:51