파이썬으로 작업하다 보면 다른 리스트에 담긴 인덱스를 기준으로 어떤 리스트의 요소들을 삭제해야 하는 경우가 종종 있습니다. 이 글에서는 그 해결 방법과 함께, 초보자가 흔히 저지르기 쉬운 실수까지 살펴보겠습니다.
문제 상황
두 개의 리스트가 있다고 가정해 보겠습니다. L1은 요소를 삭제할 대상 리스트이고, L2에는 삭제할 요소들의 인덱스가 들어 있습니다.
>>> L1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> L2 = [2, 4, 6]여기서 목표는 L2에 지정된 인덱스(2번, 4번, 6번 위치)에 있는 요소들, 즉 값 3, 5, 7을 L1에서 제거하여 [1, 2, 4, 6, 8, 9]라는 결과를 얻는 것입니다.
enumerate() 함수란?
파이썬 내장 함수 enumerate()는 리스트를 순회할 때 각 요소와 그 인덱스를 하나의 튜플로 묶어주는 enumerate 객체를 반환합니다. 이를 활용하면 인덱스와 값을 동시에 확인할 수 있어, 특정 인덱스에 해당하는 요소를 찾는 데 매우 유용합니다.
>>> for i, j in enumerate(L1):
print(i, j)주의: 반복 중에 요소를 삭제하면 인덱스가 어긋납니다
리스트를 앞쪽부터 순회하면서 del로 요소를 삭제하면, 뒤에 있던 요소들이 한 칸씩 앞으로 당겨지면서 인덱스가 밀리게 됩니다. 그 결과 원래 의도했던 것과 다른 요소가 삭제되어 [1, 2, 4, 5, 7, 8]처럼 엉뚱한 결과가 나올 수 있습니다. 따라서 아래에서 소개하는 두 가지 올바른 방법을 사용하는 것이 좋습니다.
올바른 방법 1: 리스트 컴프리헨션 사용
원본 리스트를 직접 수정하지 않고, 조건에 맞는 요소만 골라 새 리스트를 만드는 것이 가장 안전하고 파이썬다운 방법입니다.
>>> L1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> L2 = [2, 4, 6]
>>> L1 = [x for i, x in enumerate(L1) if i not in L2]
>>> L1
[1, 2, 4, 6, 8, 9]enumerate()로 인덱스와 값을 함께 꺼낸 뒤, 인덱스가 L2에 없는 경우만 남기면 됩니다. 그러면 인덱스 2, 4, 6에 해당하는 요소 3, 5, 7이 정확히 제거됩니다.
올바른 방법 2: 뒤에서부터 삭제하기
반드시 원본 리스트 자체를 수정해야 한다면, 인덱스를 내림차순으로 정렬한 뒤 삭제하면 됩니다. 뒤쪽부터 삭제하면 앞쪽 요소들의 인덱스는 변하지 않으므로 안전합니다.
>>> L1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> L2 = [2, 4, 6]
>>> for i in sorted(L2, reverse=True):
del L1[i]
>>> L1
[1, 2, 4, 6, 8, 9]성능 팁
L2의 크기가 크다면 멤버십 검사(in) 속도를 높이기 위해 L2를 집합(set)으로 변환하는 것이 좋습니다. 집합의 평균 검색 시간은 O(1)로 리스트보다 훨씬 빠릅니다.
>>> s = set(L2)
>>> L1 = [x for i, x in enumerate(L1) if i not in s]정리
리스트를 순회하면서 동시에 삭제하면 인덱스가 어긋나 잘못된 요소가 제거될 수 있습니다.
가장 안전한 방법은 리스트 컴프리헨션으로 새 리스트를 만드는 것입니다.
원본을 직접 수정해야 할 경우에는 인덱스를 내림차순으로 정렬해 뒤에서부터 삭제하세요.