파이썬 반복문에서 리스트 삭제시 유의점

개요

오늘은 파이썬에서 많이 사용되는 자료형 (Data Type) 인 list삭제 방법에 대해 깊이 살펴보고자 합니다.  

리스트 추가, 삽입, 검색은 비교적 쉽게 구현 가능하지만, 삭제는 상황에 따라 생각할 부분이 많습니다.

[리스트의 다양한 삭제 경우]

  • 리스트 특정 값 1개 삭제

  • 리스트 인덱스 기반 (몇 번째 요소인지) 삭제

  • 리스트 특정 값 전체 삭제

  • 리스트 복사본을 이용한 삭제 

  • 지능형 리스트 (List Comprehension) 를 이용한 삭제


1. 리스트 값에 의한 삭제

아래 리스트에서 'b' 라는 값을 삭제하고 싶은 경우입니다.

lst = ['a', 'b', 'b', 'c', 'd']

# 특정값 삭제 ('b')
lst.remove('b')
print(lst)

list class의 멤버함수 (Method) remove를 활용한 방법이며 결과는 다음과 같습니다.


결과를 보면 'b'가 2개인데 모두 삭제되지 않고 1개만 삭제된 모습이 좀 의아합니다.

list class의 remove 함수의 도움말을 보면 아래와 같이 설명되어 있습니다.

"Remove first occurrence of value."

즉 값이 처음 발생한 경우만 삭제된다는 의미이며, 여러값을 한꺼번에 삭제할 수 없습니다.


2. 리스트 인덱스에 의한 삭제

아래 리스트에서 1번째 인덱스의 값을 삭제하고 싶은 경우입니다.

(인덱스는 0부터 시작하므로 1번째 인덱스는 리스트의 두번째 값임을 유의)

lst = ['a', 'b', 'b', 'c', 'd']

# 인덱스 기반 삭제
del(lst[1])
print(lst)

파이썬 내장함수 (Built-in) del 을 활용한 방법이며 결과는 아래와 같습니다.

결과를 보면 리스트 안 어떤 값인지는 모르지만 1번째 인덱스의 값을 삭제한 모습입니다.


3. 리스트 특정값 전체 삭제

아래 리스트를 반복문에서 순회하며, 모든 'b'를 삭제하고 싶은 경우입니다.

그런데 아래와 같이 코드를 작성하는 경우 오류가 발생합니다.

lst = ['a', 'b', 'b', 'c', 'd']

for i in range(len(lst)):
    if lst[i]=='b':
        del(lst[i])

print(lst)

왜냐하면 반복문에서 리스트를 순회하며 특정값을 삭제하는 경우, 삭제 즉시 해당 리스트의 크기가 -1 만큼 감소되기 때문입니다.

즉, for문의 처음에 정의한 리스트의 길이, len(lst) 는 5 로 고정되지만 'b'를 만나 삭제되며 리스트의 길이가 4로 (-1만큼) 변하게 됩니다.

따라서 for문을 반복하며 i가 4되는 시점에 리스트의 4번 인덱스는 없으므로 아래와 같이 오류 (리스트 인덱스 범위 초과)가 발생하게 됩니다.

아래 그림을 보면 좀 이해가 쉽습니다.


다른 방법으로, 반복자 (Iterator)로 리스트를 순회하며 삭제를 시도해 보겠습니다.

lst = ['a', 'b', 'b', 'c', 'd']

for v in lst:
    if v=='b':
        lst.remove(v)        

print(lst)

결과는 아래와 같으며, 오류는 없지만 리스트의 'b' 값의 전체 삭제가 일어나지 않습니다.


물론 특정값을 전체 삭제하는 방법은 존재하며 아래에서 살펴보겠지만, 이런 부분은 C++에 비해 좀 불편합니다.

C++의 경우, 루프 내부에서 삭제가 일어난 경우 반복자 (Iterator)의 증가 연산자(++)를 프로그래머가 직접 제어해 반복자의 포인터가 증가되지 않게 할 수 있으며, 삭제가 일어나지 않은 경우에만 반복자 포인터를 증가해 다음으로 순회가 가능합니다.

만약 파이썬에서도 반복자의 증감을 루프에서 제어하는 방법이 있다면 댓글로 알려주시면 감사하겠습니다.


4. 리스트 특정값 전체 삭제 (개선)

가장 쉬운 방법은 지능형 리스트 (List Comprehension)를 이용하는 방법입니다.

lst = ['a', 'b', 'b', 'c', 'd']

lst = [v for v in lst if v!='b']   

print(lst)

결과는 아래와 같이 모든 'b' 가 삭제되었습니다.

(사실은 재구성된 리스트라고 표현해야 하겠죠)

간단하게 한줄로 구성가능하며, 쉽습니다.

 

다른 방법으로, 리스트의 복사본을 이용해 진행해 보겠습니다.

lst = ['a', 'b', 'b', 'c', 'd']

for v in lst[:]:
    if v=='b':
        lst.remove(v)

print(lst)

결과는 아래와 같이 모든 'b' 가 삭제되었습니다.

이 방법은 리스트 반복자의 복제본을 이용해 원본을 삭제하는 방식으로 진행됩니다.


이상으로 모든 설명을 마칩니다.

감사합니다.

댓글

이 블로그의 인기 게시물

Qt Designer 설치하기

PyQt5 기반 동영상 플레이어앱 만들기