developer tip

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

copycodes 2020. 9. 28. 09:20
반응형

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


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

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

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


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

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

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

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

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

이해력 대신에 itertools. Python 2 :

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

또는 Python 3에서 :

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

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

다행히도 목록 이해의 속도와 필요한 내부 변경의 의미를 모두 얻는 것은 매우 쉽습니다.

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

다른 답변과의 미묘한 차이점에 유의하십시오. 이것은 베어 이름에 할당하는 것이 아닙니다. 전체 목록이되는 목록 조각에 할당하여 하나의 참조를 다시 배치하는 대신 동일한 Python 목록 객체 내의 목록 내용 을 대체합니다. (이전 목록 개체에서 새 목록 개체로) 다른 답변과 마찬가지로.


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

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

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

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

뒤로 가야합니다. 그렇지 않으면 앉아있는 나뭇 가지를 자르는 것과 비슷합니다. :-)

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


그러한 예에 대한 최선의 접근 방식은 목록 이해입니다.

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)

공식 Python 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']

Python 2 문서 7.3. "for 문"

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

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

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

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

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

최상의 해결 방법

어느 한 쪽:

  • 처음부터 새 어레이를 시작 .append()하고 끝에서 다시 시작 : https://stackoverflow.com/a/1207460/895245

    이 시간은 효율적이지만 반복 중에 배열의 복사본을 유지하므로 공간 효율성이 떨어집니다.

  • del색인과 함께 사용 : https://stackoverflow.com/a/1207485/895245

    이것은 배열 사본을 분배하기 때문에 공간 효율적이지만 CPython 목록 이 동적 배열로 구현 되기 때문에 시간 효율성이 떨어집니다 .

    즉, 항목을 제거하려면 다음 항목을 모두 O (N) 단위로 다시 이동해야합니다.

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

파이썬이 더 잘할 수 있을까요?

이 특정 Python API가 개선 될 수있는 것 같습니다. 예를 들어 Java에 해당하는 ListIterator 와 비교 하면 반복자 자체를 제외하고는 반복되는 목록을 수정할 수 없음을 명확하게 알 수 있으며 목록을 복사하지 않고이를 수행 할 수있는 효율적인 방법을 제공합니다.

아마도 기본 원리는 자바 모두 더 좋은 인터페이스 계층 갖는다 파이썬 목록은 어쨌든 비효율적 시간 백업 동적 배열로 간주되고, 따라서 제거의 임의의 유형이 될 것이라는 것이다 ArrayListLinkedList의 구현 ListIterator.

Python stdlib에도 명시적인 연결 목록 유형이없는 것 같습니다. Python 연결 목록


함수형 프로그래밍을 좋아하는 사람들을 위해 :

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

또는

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

거대한 목록으로이 작업을 수행해야했고 목록을 복제하는 것은 비용이 많이 들었습니다. 특히 제 경우에는 남아있는 항목에 비해 삭제 횟수가 적기 때문입니다. 저는이 낮은 수준의 접근 방식을 취했습니다.

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

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


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

그래서:

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

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

originalList[:] = newList

참고, Python 문서에서 :

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

copy.deepcopy (x) x의 전체 복사본을 반환합니다.


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

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

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)

첫 번째 문제는 'a'와 'b'를 모두 remove ()에 전달하지만 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)]

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

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

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과 동일한 객체에 대해 L2를 참조했습니다. 단순히 "같음"(==)과 다른 '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'개체를 반환합니다. 실용적인 목적을 위해, 당신은 그것을 주장의 역본을 만드는 것으로 생각할 수 있습니다. 이것이 제가 추천하는 솔루션입니다.


반복 중에 다른 작업을 수행하려면 인덱스 (예 : 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나중에 삭제할 인덱스가 변경되지 않도록하기위한 것입니다.


filter()기본 제공으로 사용할 수 있습니다.

자세한 내용은 여기에서 확인하세요.


여기에있는 대부분의 답변은 목록의 사본을 생성하기를 원합니다. 목록이 상당히 긴 사용 사례 (110K 항목)가 있었고 대신 목록을 계속 줄이는 것이 더 현명했습니다.

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

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

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


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)

이렇게하면 인덱스가 정렬되고 목록 업데이트로 인해 영향을받지 않습니다 (cur 요소를 팝하는지 여부에 관계없이).


하나의 가능한 솔루션은 일부 항목을 제거 할뿐만 아니라 단일 루프의 모든 요소로 작업을 수행하려는 경우에 유용합니다.

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

비슷한 작업을 수행해야했고 제 경우 문제는 메모리였습니다. 새 개체로 몇 가지 작업을 수행 한 후 목록 내에서 여러 데이터 집합 개체를 병합해야했고 병합하려는 각 항목을 제거해야했습니다. 그들 모두를 복제하고 메모리를 폭파하지 마십시오. 제 경우에는 목록 대신 사전에 객체가 있으면 잘 작동했습니다.

```

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

```


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'

가능하면 반복하는 동안 반복 가능 항목을 수정할 필요가없는 다른 방법을 사용하는 것이 가장 좋지만 일부 알고리즘의 경우 그렇게 간단하지 않을 수 있습니다. 따라서 원래 질문에서 설명한 코드 패턴을 정말로 원한다고 확신하면 가능합니다.

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


전체 답변 :

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

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

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

목록의 현재 인덱스 값에 액세스하려면 enumerate를 사용할 수 없습니다. 이는 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]

가장 효과적인 방법은 지능형리스트는, 많은 사람들이 물론, 그것은 또한 얻을 수있는 좋은 방법입니다, 자신의 경우를 보여입니다 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]

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


다른 답변은 반복되는 목록에서 삭제하는 것이 일반적으로 나쁜 생각이라는 정답입니다. 역 반복은 함정을 피하지만 그렇게하는 코드를 따르는 것이 훨씬 더 어렵 기 때문에 일반적으로 목록 이해 또는 filter.

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

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

목록의 첫 번째 항목에서 일부 조건을 충족하는 부작용이있는 일부 작업을 수행 한 다음 즉시 목록에서 해당 항목을 제거 할 때 목록 이해보다 이해하기가 더 쉽습니다.


문제를 해결하기 위해 세 가지 접근 방식을 생각할 수 있습니다. 예를 들어, 무작위 튜플 목록을 만들 것 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조건을 충족하는 튜플을 삭제 하기 위해 작은 코드를 사용합니다 . 그러나이 메서드는 연속적으로 배치 된 두 개의 튜플이 주어진 조건을 충족하는 경우 튜플 (상기 조건을 충족 함)을 놓칠 것입니다.

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


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

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

그래서 루프가 이전 인덱스로 이동 한 이후 65 개를 포함하지 않습니다.

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

ite = lis #dont do it will reference instead copy

그래서 목록을 복사하십시오. list[::]

이제 당신은 줄 것입니다.

[5, 7, 13, 29]

문제는 반복 중에 목록에서 값을 제거한 다음 목록 색인이 축소된다는 것입니다.

대신 이해력을 발휘할 수 있습니다.

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


정말 큰 잠재력이있는 모든 것에 대해 다음을 사용합니다.

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)

그것은 다른 어떤 것보다 훨씬 빨라야합니다.


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

다음은 목록을 미리 복사하는 것이 잘못되고 역방향 반복이 불가능하며 목록 이해도 옵션이 아닌 예입니다.

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

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

for i in li:
    i = None

for elem in li:
    if elem is None:
        continue

이렇게하면 목록을 복사 할 필요가 없으며 이해하기가 더 쉽습니다.


특정 기준을 충족하는 목록에서 튜플을 반복하고 삭제할 때 참조로 사용할 수 있도록 목록의 복사본을 즉시 만들 수 있습니다.

그런 다음 제거 된 튜플의 목록이든 제거되지 않은 튜플의 목록이든 출력에 대해 원하는 목록 유형에 따라 다릅니다.

David가 지적했듯이 제거하고 싶지 않은 요소를 유지하려면 목록 이해를 권장합니다.

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

숫자 목록을 작성하고 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)

참고 URL : https://stackoverflow.com/questions/1207406/how-to-remove-items-from-a-list-while-iterating

반응형