2013-05-14 8 views
28

Mam zestaw myset i mam funkcję, która iteruje nad nim, aby wykonać operację na jego elementach i ta operacja ostatecznie usuwa element z zestawu.usuwać elementy z zestawu podczas iteracji nad nim

Oczywiście nie mogę tego zrobić, wciąż powtarzając cały pierwotny zestaw. Mogę jednak to zrobić:

mylist = list(myset) 
for item in mylist: 
    # do sth 

Czy istnieje lepszy sposób?

+2

Czy operację * zawsze * usunąć element lub tylko czasami? Jeśli zawsze usuniesz przedmiot, łatwiej będzie opróżnić go po zakończeniu iteracji. – Blckknght

Odpowiedz

14

Po pierwsze, za pomocą zestawu, jak Zero Pireus powiedział nam, możesz

myset = set([3,4,5,6,2]) 
while myset: 
    myset.pop() 
    print myset 

Dodałem metodę drukowania podając te wyjścia

>>> 
set([3, 4, 5, 6]) 
set([4, 5, 6]) 
set([5, 6]) 
set([6]) 
set([]) 

Jeśli chcesz pozostać przy wyborze listy, sugeruję, abyś głęboko skopiował listę za pomocą listy zrozumiałej i pętli po kopii, usuwając elementy z oryginalnej listy. W moim przykładzie zmniejsza się długość oryginalnej listy w każdej pętli.

l = list(myset) 
l_copy = [x for x in l] 
for k in l_copy: 
    l = l[1:] 
    print l 

daje

>>> 
[3, 4, 5, 6] 
[4, 5, 6] 
[5, 6] 
[6] 
[] 
9

To powinno działać:

while myset: 
    item = myset.pop() 
    # do something 

Lub, jeśli trzeba usunąć elementy warunkowo:

def test(item): 
    return item != "foo" # or whatever 

myset = set(filter(test, myset)) 
+1

działa to dobrze, jeśli chcę tylko usunąć wszystko z zestawu. W moim przypadku muszę pobrać każdy element i zastosować do niego funkcję ** przed ** usunięciem go z zestawu. – skyork

+0

@skyork usuwanie elementów z iterables podczas iteracji nad nimi jest złym pomysłem (wystarczająco złe, że Python zgłosi wyjątek, jeśli to działa, to właśnie robisz). Lepiej zbuduj nowy zestaw, a potem wyrzuć stare. –

+2

"Usuwanie elementów z iterables podczas ich iteracji jest złym pomysłem (wystarczająco złe, że Python podniesie wyjątek, jeśli to działa, to jest to, co robisz" - powszechne nieporozumienie, ale nieprawdziwe.) Dla niektórych iterables (hashtable- oparte w szczególności), nie jest legalne (uwaga: nie "zły pomysł"), a Python poinformuje o tym przy próbie. Dla innych iteracji jest to całkowicie poprawne i, gdy już zrozumiesz, jak to działa, przewidywalne – kindall

4

Wróćmy wszystkie liczby parzyste podczas modyfikowania istniejącego zbioru.

myset = set(range(1,5)) 
myset = filter(lambda x:x%2==0, myset) 
print myset 

Wrócimy

>>> [2, 4] 

Jeśli jest użycie okazja zawsze używaćlambda to ułatwić Ci życie.

+3

Nie ag Ree, że 'lambda' zawsze ułatwia ci życie ... Właściwie wygląda na to, że zazwyczaj nie ułatwia ci życia. . . – mgilson

1

"Oczywiście, nie mogę tego zrobić, ciągle powtarzając cały zestaw."

Nie jestem pewien, czy to prawda ... Spodziewałem się błędów "współbieżności", gdy próbowałem, ale wydaje się, że nie ma żadnych. Dla przypomnienia: kod używam wygląda następująco:

for member in myset: 
    myset.remove(member) 

(„członek” jest lepszym wyborem od nazwy zmiennej dla zbiorów; „Element” dla list)

Ach ... po prostu widziałem komentarz od uprzejmości pod odpowiedzią Zero P: najwyraźniej uprzejmie jest ekspertem, w którym jestem bumblerem, ale i tak opublikuję odpowiedź tylko po to, aby zwrócić uwagę na punkt ...

NB Zero P uznaje twierdzenie kindall, że jest w porządku z wrogością ... ale biorąc pod uwagę, że zestawy są, z założenia, nieuporządkowane, myślę, że możemy stwierdzić, że błędy współbieżności powinny (i czy? w Pythonie?) pojawiać się tylko wtedy, gdy usuwane są z n uporządkowana kolekcja (tj. lista - i nawet wtedy możesz użyć odliczania wstecznego indeksu, aby uniknąć problemu).

Byłoby zatem pojawiają że awersja do delecji-while-iteracji jest kwestią przesądów i/lub kaca od złych doświadczeń ze źle realizowanych strukturami w innych językach.

Ostateczna myśl: zestawy i iteracje naprawdę nie pasują do siebie strasznie dobrze: ponieważ zbiór jest nieuporządkowany, możesz tylko powtarzać w sposób losowy podzbiór wszystkich członków (w szczególności podzbiór "wszystko" lub "sam") !). Zestawy są zoptymalizowane pod kątem testów równości i duplikowania, co odzwierciedla ich zamierzone zastosowanie. (? Idealne rozwiązanie)

więc równoważny kawałek kodu do powyższego jest:

while myset: 
    member = myset.pop() 
    # do something 
+3

Kiedy używam pierwszej metody, otrzymuję komunikat "RuntimeError: Ustaw zmieniony rozmiar podczas iteracji" w python2.7 i 3.5 (a druga metoda opróżnia zestaw). – Mark

2

Innym sposobem może być:

s=set() 
s.add(1) 
s.add(2) 
s.add(3) 
s.add(4) 
while len(s)>0: 
    v=next(iter(s)) 
    s.remove(v) 
+0

Nie rozumiem, jak to próbuje odpowiedzieć na pytanie? – Carpetsmoker

+0

Teraz może mieć sens: D..lost połączenie podczas edycji. –

+0

Jest to w zasadzie takie samo, jak sugestia @ zero-piraeus po prostu wolniejsza. – SleepProgger

Powiązane problemy