반복하는 동안 목록에서 항목을 제거하는 방법은 무엇입니까?


934

파이썬에서 튜플 목록을 반복하고 특정 기준을 충족하면 제거하려고합니다.

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

대신에 무엇을 사용해야 code_to_remove_tup합니까? 이 방식으로 항목을 제거하는 방법을 알 수 없습니다.


이 페이지의 대부분의 답변은 왜 목록을 반복하는 동안 요소를 제거하면 이상한 결과가 나오는지 설명하지 않지만 이 질문에 대한 대답은 이며 처음 으로이 문제를 겪는 초보자에게는 더 나은 속임수입니다.
ggorlen

답변:


827

목록 이해를 사용하여 제거하지 않으려는 요소 만 포함하는 새 목록을 만들 수 있습니다.

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

또는 slice에 할당 somelist[:]하여 기존 항목을 변경하여 원하는 항목 만 포함 할 수 있습니다.

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

이 방법은 somelist변경 사항을 반영해야하는 다른 참조가있는 경우 유용 할 수 있습니다 .

이해하는 대신을 사용할 수도 있습니다 itertools. 파이썬 2에서 :

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

또는 파이썬 3에서 :

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

명확성을 기하기 위해 그리고 [:]hackish 또는 fuzzy 표기법을 사용하는 사람들을 위해 보다 명확한 대안이 있습니다. 이론적으로는 위의 한 줄짜리 라이너와 공간 및 시간과 동일한 성능을 발휘해야합니다.

temp = []
while somelist:
    x = somelist.pop()
    if not determine(x):
        temp.append(x)
while temp:
    somelist.append(templist.pop())

또한 최소한의 수정으로 Python 목록의 항목 바꾸기 기능 이 없을 수있는 다른 언어에서도 작동합니다 . 예를 들어, 모든 언어 False가 파이썬처럼 빈 목록을 캐스트 하지는 않습니다. while somelist:보다 명시적인 것을 대신 할 수 있습니다 while len(somelist) > 0:.


4
몇 개만 삭제 될 것임을 알고 있다면 더 빨리 만들 수 있습니까?
highBandWidth

20
내 목록이 너무 커서 사본을 만들 수 없으면 어떻게합니까?
jpcgt

15
@jpcgt somelist[:] = (x for x in somelist if determine(x))불필요한 사본을 만들 수없는 생성기를 생성 해야합니다 .
Rostislav Kondratenko

8
@RostislavKondratenko : 내부적으로 호출 list_ass_slice()을 구현하는 함수 . 이 함수는 항상리스트를 반환합니다. 즉 생성기 대신 이미리스트를 사용하는 @Alex Martelli의 솔루션이 가장 효율적일 것입니다.somelist[:]=PySequence_Fast()
jfs

6
목록 이해를 목록에 할당하는 것과 목록 복제의 차이점이 무엇인지 설명하고 싶습니까? somelist두 방법 모두에서 원본 목록 이 변경 되지 않습니까?
보웬 리우

589

목록 이해를 제안하는 답변은 완전히 정확합니다. 단지 완전히 새로운 목록을 작성한 다음 이전 목록과 동일한 이름을 지정하면 이전 목록을 수정하지 않습니다. @Lennart의 제안 에서와 같이 선택적 제거로 수행하는 것과는 다릅니다. 더 빠르지 만 여러 참조를 통해 목록에 액세스하면 참조 중 하나만 다시하고 목록 객체를 변경하지 않는다는 사실 그 자체는 미묘하고 비참한 버그로 이어질 수 있습니다.

다행스럽게도 목록 이해 속도와 내부 변경에 필요한 의미를 모두 쉽게 얻을 수 있습니다.

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

다른 답변과의 미묘한 차이점에 유의하십시오.이 이름은 베어 이름에 할당되지 않습니다. 목록 전체에 발생하는 목록 조각에 할당되므로 하나의 참조를 다시 가져 오는 대신 동일한 Python 목록 객체 내에서 목록 내용 을 대체합니다. 다른 답변과 같이 (이전 목록 객체에서 새 목록 객체로).


1
dict로 동일한 슬라이스 할당을 어떻게 수행합니까? 파이썬 2.6에서?
PaulMcG

11
@Paul : dicts는 순서가 없기 때문에 조각은 dicts에 의미가 없습니다. dict의 내용을 dict의 내용으로 바꾸 a려면를 b사용하십시오 a.clear(); a.update(b).
Sven Marnach

1
변수를 참조하여 버그를 일으키는 것을 대체하여 참조 중 하나를 '재 부착'할 수있는 이유 다중 스레드 응용 프로그램에서는 단일 스레드가 아닌 잠재적 인 문제 일 것 같습니다.
Derek Dahmer

59
@Derek x = ['foo','bar','baz']; y = x; x = [item for item in x if determine(item)];이것은 x목록 이해 결과에 다시 할당 되지만 y여전히 원래 목록을 참조 합니다 ['foo','bar','baz']. 동일한 목록 을 예상 x하고 y참조하는 경우 버그가 발생한 것일 수 있습니다. Alex가 보여주는 것처럼 전체 목록의 한 조각에 할당하여 이것을 방지하고 여기에 표시합니다 x = ["foo","bar","baz"]; y = x; x[:] = [item for item in x if determine(item)];. 목록이 수정되었습니다. 목록 ( xy여기)에 대한 모든 참조가 새 목록을 참조하는지 확인하십시오.
Steven T. Snyder

실제로, filter함수를 사용 하면 새로운리스트가 생성되고, 요소를 수정하지 않습니다 ...olist[:] = [i for i in olist if not dislike(i)]
John Strood

302

목록의 사본을 가져 와서 먼저 반복해야합니다. 그렇지 않으면 예상치 못한 결과로 인해 반복이 실패합니다.

예를 들어 (목록 유형에 따라 다름) :

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

예를 들면 :

>>> 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
[]

13
@Zen 두 번째 것은 목록의 사본을 반복하기 때문입니다. 따라서 원본 목록을 수정할 때 반복하는 사본은 수정하지 않습니다.
Lennart Regebro

3
list (somelist)와 비교하여 somelist [:]을 사용하는 것이 더 낫습니까?
Mariusz Jamro

3
list(somelist)iterable을 목록으로 변환합니다. somelist[:]슬라이싱을 지원하는 객체의 복사본을 만듭니다. 그래서 그들은 반드시 같은 것을 할 필요는 없습니다. 이 경우 나는의 사본을 만들고 싶어 somelist내가 사용하는, 그래서 개체[:]
레나 Regebro

33
이 글을 읽는 사람에게, 이것은 목록에 대해 매우 느립니다. remove()반복 할 때마다 WHOLE 목록을 검토해야하므로 시간이 오래 걸립니다.
vitiral

7
12 개의 항목 만 처리 할 때는 큰 O 시간이 중요하지 않습니다. 미래의 프로그래머가 이해하는 것이 명확하고 단순한 경우가 성능보다 훨씬 더 중요합니다.
Steve

127
for i in range(len(somelist) - 1, -1, -1):
    if some_condition(somelist, i):
        del somelist[i]

그렇지 않으면 거꾸로 가야합니다. 그렇지 않으면 앉아있는 나뭇 가지를 톱질하는 것과 같습니다.

파이썬 2 사용자 : 대체 range에 의해 xrange피하기 위해 하드 코딩 된 목록을 작성


13
최신 버전의 Python에서는 reversed()내장
ncoghlan

16
reversed ()는 새 목록을 만들지 않고 제공된 시퀀스에서 역 반복자를 만듭니다. enumerate ()처럼리스트를 실제로 가져 오려면 list ()로 랩핑해야합니다. 당신은 생각 될 수있다 분류 (), 이는 않습니다 (그것을 정렬 할 수 있도록, 그것은에있다) 새 목록마다 시간을 만들 수 있습니다.
ncoghlan

1
@Mauris enumerate는 반복자를 반환 reversed하고 시퀀스를 기대 하기 때문에 . reversed(list(enumerate(somelist)))메모리에 추가 목록을 작성하는 것이 마음에 들지 않으면 할 수 있다고 생각합니다 .
drevicko

2
배열의 경우 O (N * M)이며 큰 목록에서 많은 항목을 제거하면 속도가 매우 느립니다. 따라서 권장하지 않습니다.
Sam Watkins

2
@ SamWatkins 네,이 답변은 매우 큰 배열에서 몇 가지 요소를 제거 할 때입니다. 메모리 사용량은 적지 만 m시간이 느려질 수 있습니다 .
Navin

52

공식 파이썬 2 튜토리얼 4.2. "명세서"

https://docs.python.org/2/tutorial/controlflow.html#for-statements

문서 의이 부분은 다음을 분명히합니다.

  • 반복 목록을 복사하여 수정해야합니다.
  • 한 가지 방법은 슬라이스 표기법을 사용하는 것입니다. [:]

루프 내에서 반복하는 시퀀스를 수정해야하는 경우 (예 : 선택한 항목 복제) 먼저 복사하는 것이 좋습니다. 시퀀스를 반복해도 암시 적으로 복사되지 않습니다. 슬라이스 표기법은이를 특히 편리하게 만듭니다.

>>> words = ['cat', 'window', 'defenestrate']
>>> 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']

파이썬 2 문서 7.3. "for 문"

https://docs.python.org/2/reference/compound_stmts.html#for

문서 의이 부분은 다시 한 번 복사해야한다고 말하고 실제 제거 예를 제공합니다.

참고 : 루프가 시퀀스를 수정하는 경우 미묘한 부분이 있습니다 (이는 변경 가능한 시퀀스 (예 : 목록)에 대해서만 발생할 수 있음). 내부 카운터는 다음에 어떤 항목이 사용되는지 추적하는 데 사용되며 각 반복마다 증가합니다. 이 카운터가 시퀀스 길이에 도달하면 루프가 종료됩니다. 즉, 스위트가 시퀀스에서 현재 (또는 이전) 항목을 삭제하면 다음 항목은 건너 뜁니다 (이미 처리 된 현재 항목의 색인을 가져 오기 때문에). 마찬가지로 제품군이 현재 항목 앞에 순서대로 항목을 삽입하면 다음에 루프를 통해 현재 항목이 다시 처리됩니다. 이로 인해 전체 시퀀스 조각을 사용하여 임시 복사본을 만들어 피할 수있는 불쾌한 버그가 발생할 수 있습니다.

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

그러나 값을 찾기 위해 전체 목록.remove()반복해야하기 때문에이 구현에 동의하지 않습니다 .

최상의 해결 방법

어느 한 쪽:

  • https://stackoverflow.com/a/1207460/895245.append() : 새 배열을 처음부터 새로 시작한 후 다시 시작

    이 시간은 효율적이지만 반복하는 동안 어레이의 사본을 유지하기 때문에 공간이 덜 효율적입니다.

  • del인덱스와 함께 사용 : https : //.com/a/1207485/895245

    이것은 배열 사본을 분배하므로 공간 효율적이지만 CPython 목록 이 동적 배열로 구현 되므로 시간이 덜 효율적 입니다.

    즉, 항목을 제거하려면 모든 다음 항목을 하나씩 뒤로 이동해야합니다 (O (N)).

일반적으로 .append()메모리가 큰 문제가 아닌 한 기본적으로 더 빠른 옵션을 원합니다 .

파이썬이 더 잘 할 수 있습니까?

이 특정 Python API가 개선 될 수있는 것 같습니다. 예를 들어 다음과 비교하십시오.

두 가지 모두 반복자 자체를 제외하고 반복되는 목록을 수정할 수 없다는 것을 분명히하고 목록을 복사하지 않고 효율적으로 수행 할 수있는 방법을 제공합니다.

아마도 기본 이론적 근거는 파이썬 목록이 동적 배열을 지원한다고 가정하기 때문에 어쨌든 모든 유형의 제거는 시간이 비효율적이지만 Java는의 구현 ArrayListLinkedList구현이 모두 더 좋은 인터페이스 계층 구조를 갖습니다 ListIterator.

파이썬 stdlib에는 명시 적으로 연결된 목록 유형이없는 것 같습니다 : Python Linked List


48

이러한 예에 대한 가장 좋은 방법은 목록 이해입니다.

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

determine함수를 호출하는 것보다 복잡한 작업을 수행하는 경우 새 목록을 작성하고 단순히 추가 할 때 추가하는 것이 좋습니다. 예를 들어

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

를 사용하여 목록을 복사하면 remove아래 답변 중 하나에 설명 된대로 코드가 좀 더 깔끔해 보일 수 있습니다. 목록 전체를 먼저 복사하고 O(n) remove제거 할 각 요소에 대해 작업을 수행 하여 O(n^2)알고리즘으로 만들기 때문에 매우 큰 목록에 대해서는이 작업을 수행하지 않아야 합니다.

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

37

기능적 프로그래밍을 좋아하는 사람들을 위해 :

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

또는

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

1.리스트 이해 및 생성기 표현은 순수한 기능 언어 인 Haskell에서 빌려온 것입니다. 그것들은 똑같이 기능적 filter이며 Pythonic입니다. 2. 당신은 필요한 경우 lambda사용 map또는 filter목록 빌려 또는 genexpr이며, 항상 더 나은 옵션; mapfilter변환 / 술어 함수는 파이썬이 내장에서 C로 구현하고 반복 가능한이 하찮게 작은 아니지만, 당신이 필요로 할 때 그들은 항상 느린있을 때 약간 빠를 수 있습니다 lambdalistcomp / genexpr은 피할 수있다.
ShadowRanger

13

거대한 목록 으로이 작업을 수행해야했고 목록을 복제하는 것이 비용이 많이 드는 것처럼 보였습니다. 특히 필자의 경우 삭제 항목 수가 남아있는 항목과 비교할 때 적기 때문입니다. 나는이 저수준 접근법을 취했다.

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

내가 모르는 것은 큰 목록을 복사하는 것보다 몇 번의 삭제가 얼마나 효율적인지입니다. 통찰력이 있으면 의견을 말하십시오.


제 경우에는 그 '원치 않는'요소를 다른 목록으로 옮겨야합니다. 이 솔루션에 대한 새로운 의견이 있습니까? 또한 목록을 복제하는 대신 일부 삭제를 사용하는 것이 좋습니다.
gustavovelascoh

@Alexey와 동일하지만 성능에 문제가있는 경우 정답입니다. 즉, list리스트의 중간에서 제거하는 것은리스트의 길이에 선형 시간이 걸리기 때문에, 우선 데이터 구조로서의 선택은 신중하게 고려되어야한다. k 번째 순차 항목에 대한 임의 액세스가 실제로 필요하지 않은 경우 고려할 사항이 OrderedDict있습니까?
최대

@GVelascoh 왜 newlist = []그리고 newlist.append(array[i])바로 전에 만들지 del array[i]않습니까?
최대

2
이것은 시간이 비효율적 일 수 있음에 유의하십시오 list(). 링크 된 목록 인 경우 무작위 액세스는 비싸고, list()배열 인 경우 에는 다음 요소를 모두 앞으로 이동해야하기 때문에 삭제가 비쌉니다. 알맞은 반복자는 연결 목록 구현에 좋은 것을 만들 수 있습니다. 그러나 이것은 공간 효율적일 수 있습니다.
Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功

10

현재 목록 항목이 원하는 기준을 충족하는 경우 새 목록을 만드는 것이 좋습니다.

그래서:

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

새 목록 이름으로 전체 프로젝트를 다시 코딩하지 않아도됩니다.

originalList[:] = newList

파이썬 문서에서 참고하십시오 :

copy.copy (x) x의 얕은 복사본을 반환합니다.

copy.deepcopy (x) x의 깊은 사본을 반환합니다.


3
이것은 몇 년 전에 받아 들여진 답변에 없었던 새로운 정보를 추가하지 않습니다.
Mark Amery

2
@MarkAmery 문제를 보는 간단하고 또 다른 방법입니다. 압축 코딩 구문을 좋아하지 않는 사람들에게는 덜 압축되어 있습니다.
ntk4

9

이 답변은 원래 중복으로 표시 된 질문에 대한 답변으로 작성되었습니다 .python의 목록에서 좌표 제거

코드에는 두 가지 문제가 있습니다.

1) remove ()를 사용할 때 정수를 제거하려고 시도하지만 튜플을 제거해야합니다.

2) for 루프는 목록의 항목을 건너 뜁니다.

코드를 실행할 때 어떤 일이 발생하는지 살펴 보겠습니다.

>>> 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)

첫 번째 문제는 remove ()에 'a'와 'b'를 모두 전달하지만 remove ()는 단일 인수 만 허용한다는 것입니다. 그렇다면 목록에서 제대로 작동하도록 remove ()를 어떻게 얻을 수 있습니까? 리스트의 각 요소가 무엇인지 파악해야합니다. 이 경우, 각각은 튜플입니다. 이를 확인하려면 목록의 한 요소에 액세스하십시오 (인덱싱은 0에서 시작).

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

아하! L1의 각 요소는 실제로 튜플입니다. 이것이 바로 remove ()에 전달해야하는 것입니다. 파이썬의 튜플은 매우 쉽습니다. 간단히 괄호 안에 값을 넣는 것입니다. "a, b"는 튜플이 아니지만 "(a, b)"는 튜플입니다. 따라서 코드를 수정하고 다시 실행하십시오.

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

이 코드는 오류없이 실행되지만 출력되는 목록을 살펴 보겠습니다.

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

왜 (1, -2)가 여전히 목록에 있습니까? 루프를 사용하여 목록을 반복하는 동안 목록을 수정하는 것은 특별한주의없이 매우 나쁜 생각입니다. (1, -2)가리스트에 남아있는 이유는리스트 내의 각 항목의 위치가 for 루프의 반복 사이에서 변경 되었기 때문입니다. 위의 코드에 더 긴 목록을 제공하면 어떻게되는지 봅시다 :

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)]

해당 결과에서 유추 할 수 있듯이 조건문이 true로 평가되고 목록 항목이 제거 될 때마다 루프의 다음 반복은 값이 다른 색인에 있으므로 목록의 다음 항목에 대한 평가를 건너 뜁니다.

가장 직관적 인 해결책은 목록을 복사 한 다음 원본 목록을 반복하고 사본 만 수정하는 것입니다. 다음과 같이 해보십시오.

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)

그러나 출력은 이전과 동일합니다.

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

L2를 만들 때 파이썬이 실제로 새 객체를 만들지 않았기 때문입니다. 대신, L1을 L1과 동일한 오브젝트로 참조했습니다. 단순히 "같음"(==)과 다른 'is'로이를 확인할 수 있습니다.

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

copy.copy ()를 사용하여 실제 복사본을 만들 수 있습니다. 그런 다음 모든 것이 예상대로 작동합니다.

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)]

마지막으로 완전히 새로운 L1 사본을 만드는 것보다 하나의 더 깨끗한 솔루션이 있습니다. reversed () 함수 :

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)]

불행히도 reversed () 작동 방식을 적절하게 설명 할 수 없습니다. 리스트가 전달 될 때 'listreverseiterator'오브젝트를 리턴합니다. 실용적인 목적으로, 당신은 그것의 주장의 역전 된 사본을 만드는 것으로 생각할 수 있습니다. 이것이 내가 권장하는 솔루션입니다.


4

반복하는 동안 다른 작업을 수행하려면 색인 (예를 들어 dict 목록이있는 경우 색인을 참조 할 수 있음)과 실제 목록 항목 컨텐츠를 모두 얻는 것이 좋습니다.

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한 번에 항목과 색인에 액세스 할 수 있습니다. reversed나중에 삭제하려는 인덱스가 변경되지 않도록하기 위해서입니다.


다른 종류의 목록보다 dict 목록이있는 경우 색인이 더 관련성이 높은 이유는 무엇입니까? 내가 알 수있는 한 이해가되지 않습니다.
Mark Amery


4

여기에있는 대부분의 답변은 목록의 사본을 작성하기를 원합니다. 나는 목록이 꽤 길었던 유스 케이스 (110K 항목)를 가지고 있었고 대신 목록을 계속 줄이는 것이 더 똑똑했습니다.

우선 foreach 루프를 while 루프교체 해야합니다 .

i = 0
while i < len(somelist):
    if determine(somelist[i]):
         del somelist[i]
    else:
        i += 1

i이전 항목이 삭제되면 동일한 항목에서 새 항목의 값을 가져 오려고하므로 if 블록에서 값이 변경되지 않습니다.


3

for-looping을 반대로 시도하면 some_list에 대해 다음과 같은 작업을 수행 할 수 있습니다

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)

이렇게하면 인덱스가 정렬되고 목록 요소가 발생하지 않더라도 목록 업데이트가 발생하지 않습니다.


루프 오버 reversed(list(enumerate(some_list)))는 자신을 계산하는 것보다 간단합니다.
Mark Amery

@MarkAmery는이 방법으로 목록을 변경할 수 있다고 생각하지 않습니다.
Queequeg 2016 년

3

하나의 가능한 해결책은 일부를 제거 할뿐만 아니라 단일 루프에서 모든 요소로 무언가를 수행하려는 경우에 유용합니다.

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

실제로 이해력을 사용해야합니다. 이해하기가 훨씬 쉽습니다.
Beefster

내가 bad물건 을 제거 하고, 그것을 가지고 무언가를하고 또한 good하나의 루프에서 물건으로 무언가를 하고 싶다면 어떻게해야 합니까?
Alexey

1
실제로, 나는 당신이 열린 슬라이스로 목록의 사본을 만드는 점에서 약간의 영리함을 깨달았습니다 alist[:]. 좋은 개정이 좋습니다. 공감하십시오.
Beefster

2

비슷한 일을해야했고 내 경우에는 문제가 메모리였습니다. 목록에 여러 데이터 세트 객체를 병합 한 다음 새 객체로 사용하고 병합하려는 각 항목을 제거해야했습니다. 그들 모두를 복제하고 메모리를 폭파시키지 마십시오. 필자의 경우 목록 대신 사전에 객체를 갖는 것이 잘 작동했습니다.

```

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

TLDR :

나는 당신이 이것을 할 수있는 도서관을 썼습니다 :

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'

가능하면 반복 가능한 동안 iterable을 수정하지 않아도되는 다른 방법을 사용하는 것이 가장 좋지만 일부 알고리즘의 경우에는 그다지 간단하지 않을 수 있습니다. 따라서 원래 질문에 설명 된 코드 패턴을 정말로 원한다면 가능할 수 있습니다.

목록뿐만 아니라 모든 가변 시퀀스에서 작동합니다.


전체 답변 :

편집 :이 답변의 마지막 코드 예제 는 목록 이해를 사용하지 않고 대신 목록을 수정하려는 이유에 대한 사용 사례를 제공합니다 . 답변의 첫 부분은 배열 을 어떻게 수정 하는 지에 대한 자습서 역할을 합니다 .

해결책 은 senderle 의이 답변 (관련 질문에 대한)에서 따릅니다. 배열 목록이 수정 된 목록을 반복하면서 업데이트되는 방법을 설명합니다. 아래 솔루션은 목록이 수정 된 경우에도 배열 인덱스를 올바르게 추적하도록 설계되었습니다.

다운로드 fluidIter.py에서 여기 https://github.com/alanbacon/FluidIterator 필요가 자식을 설치하지 않을 수 있도록, 그것은 단지 하나의 파일입니다. 설치 프로그램이 없으므로 파일이 파이썬 경로에 있는지 확인해야합니다. 이 코드는 Python 3 용으로 작성되었으며 Python 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))

결과는 다음과 같습니다.

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]

위에서 우리는 pop유체리스트 객체에 대한 방법을 사용했습니다 . 다른 일반적인 반복 가능한 방법은 또한으로 구현 del fluidL[i], .remove, .insert, .append, .extend. 슬라이스를 사용하여 목록을 수정할 수도 있습니다 ( sortreverse메소드는 구현되지 않음).

유일한 조건은 어느 시점 fluidL에서나 l다른 목록 객체에 다시 할당 된 경우 코드가 작동하지 않는 경우에만 목록을 수정해야한다는 것입니다. 원래 fluidL객체는 여전히 for 루프에서 사용되지만 수정하기에는 범위를 벗어납니다.

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

목록의 현재 인덱스 값에 액세스하려면 열거를 사용할 수 없습니다. 이는 for 루프가 몇 번이나 실행되었는지를 계산하기 때문입니다. 대신 반복자 객체를 직접 사용합니다.

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))

이것은 다음을 출력합니다 :

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]

FluidIterable클래스는 원래 목록 객체에 대한 래퍼 만 제공합니다. 원본 객체는 다음과 같이 유동 객체의 속성으로 액세스 할 수 있습니다.

originalList = fluidArr.fixedIterable

더 많은 예제 / 테스트는 if __name__ is "__main__":하단 에있는 섹션에서 찾을 수 있습니다 fluidIter.py. 이것들은 다양한 상황에서 일어나는 일을 설명하기 때문에 가치가 있습니다. 예 : 슬라이스를 사용하여 목록의 큰 섹션 교체 또는 중첩 된 for 루프에서 동일한 iterable을 사용하고 수정합니다.

처음부터 언급했듯이 : 이것은 코드의 가독성을 손상시키고 디버그하기가 더 어려운 복잡한 솔루션입니다. 따라서 David Raznick의 답변에 언급 된 목록 이해와 같은 다른 솔루션을 먼저 고려해야합니다. 즉,이 클래스가 나에게 유용하고 삭제해야 할 요소의 색인을 추적하는 것보다 사용하기 쉬운 시간을 발견했습니다.


편집 : 의견에서 언급 했듯이이 답변은 실제로이 접근법이 해결책을 제공하는 문제를 제시하지는 않습니다. 나는 그것을 여기에서 해결하려고 노력할 것이다.

리스트 이해는 새로운리스트를 생성하는 방법을 제공하지만, 이러한 접근법은리스트의 현재 상태가 아닌 개별적으로 각 요소를 보는 경향이 있습니다.

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

그러나 그 결과 testFuncnewList이미 추가 된 요소에 의존 한다면 어떨까요? 아니면 여전히 그 요소가 oldList다음에 추가 될 수 있습니까? 여전히 목록 이해력을 사용하는 방법이있을 수 있지만 우아함을 잃기 시작할 것입니다.

아래 코드는 위의 문제가 발생하는 알고리즘의 한 예입니다. 이 알고리즘은 어떤 요소도 다른 요소의 배수가되지 않도록 목록을 줄입니다.

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)

출력 및 최종 축소 목록은 다음과 같습니다.

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]

해결하려는 문제가 확실하지 않기 때문에 이것이 과도하게 엔지니어링되었는지 여부를 말하기는 어렵습니다. 이 방법을 사용하여 요소를 제거하면 얻을 수 some_list[:] = [x for x in some_list if not some_condition(x)]없는 것은 무엇입니까? 이에 대한 답이 없으면 왜 600 줄 라이브러리를 다운로드하여 사용하는 것이 오타와 주석 처리 된 코드로 완성되는 것이 원 라이너보다 문제에 대한 더 나은 해결책이라고 생각해야합니까? -1.
Mark Amery

@MarkAmery. 항목 자체뿐 아니라 목록의 다른 항목 상태 또는 목록의 상태를 기준으로 항목을 제거 (또는 추가 또는 이동)해야하는지 여부를 결정하려는 경우의 주요 사용 사례입니다. 전부의. 예를 들어 list comprehensions를 사용 하여와 다른 list 요소가있는 some_list[:] = [x for x in some_list if not some_condition(y)]곳 과 같은 것을 작성할 수 없습니다 . 쓰기도 불가능하다 . yxsome_list[:] = [x for x in some_list if not some_condition(intermediateStateOf_some_list)]
공명

2

가장 효과적인 방법은 목록 이해입니다. 많은 사람들이 자신의 사례를 보여줍니다. 물론 iterator통과 하는 좋은 방법이기도합니다 filter.

Filter함수와 시퀀스를받습니다. Filter전달 된 함수를 각 요소에 차례로 적용한 다음 함수 반환 값이 True또는 인지에 따라 요소를 유지할지 또는 삭제할지 결정합니다 False.

예가 있습니다 (튜플에서 확률을 얻으십시오).

list(filter(lambda x:x%2==1, (1, 2, 4, 5, 6, 9, 10, 15)))  
# result: [1, 5, 9, 15]

주의 : 반복자를 처리 할 수도 없습니다. 반복자는 때때로 시퀀스보다 낫습니다.


2

for 루프는 인덱스를 반복합니다.

목록이 있다고 생각해

[5, 7, 13, 29, 65, 91]

이라는 목록 변수를 사용했습니다 lis. 같은 것을 사용하여 제거합니다 ..

당신의 변수

lis = [5, 7, 13, 29, 35, 65, 91]
       0  1   2   3   4   5   6

5 번 반복하는 동안

당신의 번호 (35)는 당신이 목록에서 제거하므로 주요 아니었다.

lis.remove(y)

다음 다음 값 (65) 이전 인덱스로 이동합니다.

lis = [5, 7, 13, 29, 65, 91]
       0  1   2   3   4   5

그래서 네 번째 반복 완료 포인터가 다섯 번째로 이동했습니다.

그렇기 때문에 루프가 이전 인덱스로 이동 한 이후 루프가 65를 커버하지 않는 이유는 무엇입니까?

따라서 복사 대신 원본을 여전히 참조하는 다른 변수로 목록을 참조해서는 안됩니다.

ite = lis #dont do it will reference instead copy

그래서 목록의 사본을 사용하여 list[::]

지금 당신은 그것을 줄 것이다

[5, 7, 13, 29]

문제는 반복하는 동안 목록에서 값을 제거하면 목록 색인이 축소되는 것입니다.

대신 이해력을 시험해 볼 수 있습니다.

목록, 튜플, dict, 문자열 등 모든 반복 가능을 지원합니다.


이를 통해 코드가 실패한 이유를 이해할 수있었습니다.
Wahid Sadik

2

반복하는 동안 목록에서 요소를 삭제하려면 while 루프를 사용하여 각 삭제 후 현재 색인 및 종료 색인을 변경할 수 있습니다.

예:

i = 0
length = len(list1)

while i < length:
    if condition:
        list1.remove(list1[i])
        i -= 1
        length -= 1

    i += 1

1

다른 답변은 반복하는 목록에서 일반적으로 삭제하는 것이 좋지 않다는 것이 맞습니다. 역 반복은 함정을 피하지만 코드를 따르는 것은 훨씬 어렵 기 때문에 일반적으로 목록 이해 또는를 사용하는 것이 좋습니다 filter.

그러나 반복하는 시퀀스에서 요소를 제거하는 것이 안전한 경우가 있습니다. 반복하는 동안 하나의 항목 만 제거하는 경우입니다. a return또는 a를 사용하여 보장 할 수 있습니다 break. 예를 들면 다음과 같습니다.

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

어떤 조건을 만족하는 목록의 첫 번째 항목에 부작용이있는 작업을 수행 한 후 바로 목록에서 해당 항목을 제거 할 때 목록 이해보다 이해하기가 더 쉽습니다.


1

문제를 해결하기위한 세 가지 접근 방식을 생각할 수 있습니다. 예를 들어 임의의 튜플 목록을 만듭니다 somelist = [(1,2,3), (4,5,6), (3,6,6), (7,8,9), (15,0,0), (10,11,12)]. 내가 선택한 조건은 sum of elements of a tuple = 15입니다. 최종 목록에는 합계가 15가 아닌 튜플 만 있습니다.

내가 선택한 것은 무작위로 선택된 예입니다. 튜플 목록 과 내가 선택한 조건자유롭게 변경 하십시오 .

방법 1.> 제안한 프레임 워크를 사용하십시오 (for 루프 안에 코드를 채우는 곳). 나는 작은 코드를 사용 del하여 상기 조건에 맞는 튜플을 삭제합니다. 그러나, 연속적으로 배치 된 2 개의 튜플이 주어진 조건을 만족시키는 경우,이 방법은 (상기 조건을 만족하는) 튜플을 놓칠 것이다.

for tup in somelist:
    if ( sum(tup)==15 ): 
        del somelist[somelist.index(tup)]

print somelist
>>> [(1, 2, 3), (3, 6, 6), (7, 8, 9), (10, 11, 12)]

방법 2.> 주어진 조건이 충족되지 않는 요소 (튜플)를 포함하는 새로운 목록을 구성하십시오 (이는 주어진 조건이 충족되는 목록의 요소를 제거하는 것과 같습니다). 다음은 그 코드입니다.

newlist1 = [somelist[tup] for tup in range(len(somelist)) if(sum(somelist[tup])!=15)]

print newlist1
>>>[(1, 2, 3), (7, 8, 9), (10, 11, 12)]

방법 3.> 주어진 조건이 충족되는 인덱스를 찾은 다음 해당 인덱스에 해당하는 요소 제거 (튜플)를 사용하십시오. 다음은 그 코드입니다.

indices = [i for i in range(len(somelist)) if(sum(somelist[i])==15)]
newlist2 = [tup for j, tup in enumerate(somelist) if j not in indices]

print newlist2
>>>[(1, 2, 3), (7, 8, 9), (10, 11, 12)]

방법 1과 방법 2는 방법 3보다 빠릅니다 . Method2와 method3은 method1보다 효율적입니다. 나는 method2 선호한다 . 전술 한 예에서,time(method1) : time(method2) : time(method3) = 1 : 1 : 1.7


0

정말로 큰 잠재력을 가진 것은 다음을 사용합니다.

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)

다른 것보다 훨씬 빠릅니다.


내가 측정 한 것으로부터 NumPy는 20 개 이상의 요소 목록에 대해 더 빠르기 시작하고 1000 개 이상의 큰 목록에 대해 12 배 이상 빠른 필터링에 도달합니다.
Georgy

0

한 번에 한 항목 씩 목록을 필터링하는 것 이상의 작업을 수행하는 경우에는 반복하는 동안 반복이 변경되기를 원합니다.

다음은 미리 목록을 복사하는 것이 올바르지 않고 역 반복이 불가능하며 목록 이해도 옵션이 아닌 예입니다.

""" 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

나중에 새 목록을 사용할 경우 elem을 없음으로 설정 한 후 다음과 같이 나중에 루프에서 판단 할 수 있습니다.

for i in li:
    i = None

for elem in li:
    if elem is None:
        continue

이런 식으로 목록을 복사 할 필요가 없으며 이해하기 쉽습니다.


-1

숫자 목록을 세우고 3으로 나눌 수있는 모든 no를 제거하려고합니다.

list_number =[i for i in range(100)]

를 사용 list comprehension하면 새 목록을 관리하고 새 메모리 공간을 만듭니다.

new_list =[i for i in list_number if i%3!=0]

lambda filter기능을 사용 하면 결과로 새로운 목록이 생성되고 밈 공간이 소비됩니다

new_list = list(filter(lambda x:x%3!=0, list_number))

새 목록을위한 메모리 공간을 소비하지 않고 기존 목록을 수정

for index, value in enumerate(list_number):
    if list_number[index]%3==0:
        list_number.remove(value)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.