파이썬의 List 자료형은 정말 정말 중요합니다.
제 생각에는 툴로써 사용할 수 있는 자료형 중 가장 많이 사용하는 것 같은데요.

리스트 자료형이 활용도가 높기 때문에 그만큼 중요성도 높은 것이겠죠.
리스트 자료형, 알아보도록 하겠습니다.

List 자료형 소개

List 자료형?

List. 직역하면 “목록” 정도가 되겠네요.
List는 말 그대로 어떠한 값이나 객체를 담는 “목록”의 역할을 하게 됩니다.

List 안에 있는 원소들은 각각 그 값 자체를 가지고 있다기보다, 그 값의 주소값을 가지고 있는 것이지만,
이런 이야기는 나중에 하기로 하고, 어떻게 사용하는지부터 보겠습니다.

List 의 선언

list는 처음부터 직접 원소와 함께 선언해줄 수도 있고,
list 변수를 먼저 선언한 후, 원소를 넣어줄 수도 있습니다.

물론 처음부터 원소와 함께 선언한 후, 나중에 추가 원소를 넣어줄 수도 있죠.

1
2
3
4
5
6
7
8
9
10
# (1) 직접 선언
list_a = [1, 2, 3]

# (2) 먼저 선언 후 값을 할당
list_b = list()
list_b.append(1)
list_b.append(2)
list_b.append(3)

### >>> [1, 2, 3] 으로 동일

보시다시피 리스트는 대괄호 ([]) 안에 원소값을 들어있는 형태가 됩니다.

List에 담기는 값

list에는 담기는 값에 제한이 없습니다.
string, int, object… 모든 값들이 담길 수 있습니다.

또한 하나의 리스트에 여러 타입의 값이 담길 수도 있습니다.

1
2
3
4
5
6
7
8
9
10
11
a = "안녕하세요. 테스트 문장입니다."

list_temp = [a, "hello", 3, 4, 3.141592, 'c', [1, 2, 3]]

print(type(list_temp[0]))   # >>> <class 'str'>
print(type(list_temp[1]))   # >>> <class 'str'>
print(type(list_temp[2]))   # >>> <class 'int'>
print(type(list_temp[3]))   # >>> <class 'int'>
print(type(list_temp[4]))   # >>> <class 'float'>
print(type(list_temp[5]))   # >>> <class 'str'>
print(type(list_temp[6]))   # >>> <class 'list'>

List 에서 값 찾기 : 인덱싱

List는 인덱싱과 슬라이싱을 지원합니다.
바로 위에서 list_temp[0] 과 같은 형식의 표현법을 보셨을텐데요, 이게 바로 인덱싱입니다.

index 색인, 즉 list의 원소들에 번호를 붙여서 그 값을 가리키는 방법이죠.
리스트 안의 원소는 0번부터 시작해서 1번, 2번 … 과 같이 번호가 매겨집니다.

1
2
3
4
5
6
7
8
a = "안녕하세요. 테스트 문장입니다."

list_temp = [a, "hello", 3, 4, 3.141592, 'c', [1, 2, 3]]

print(list_temp[0]) # >>> 안녕하세요. 테스트 문장입니다.
print(list_temp[1]) # >>> hello
print(list_temp[2]) # >>> 3
print(list_temp[3]) # >>> 4

그런데 여기서 문제!
그러면 리스트 안의 원소의 개수가 엄청 많을 때에는 어떻게 할까요?
뒤쪽에 있는 원소는 찾기가 힘들텐데..

이럴 때에는 음수(-) 인덱스를 사용하면 됩니다.
음수 인덱스는 뒤에서부터 순서를 세게 됩니다.
0은 첫 번째 원소를 가리키고 있으니, -1부터 시작하겠죠?

1
2
3
4
5
6
7
a = "안녕하세요. 테스트 문장입니다."

list_temp = [a, "hello", 3, 4, 3.141592, 'c', [1, 2, 3]]

print(list_temp[-1]) # >>> [1, 2, 3]
print(list_temp[-2]) # >>> c
print(list_temp[-3]) # >>> 3.141592

여기에 추가로, 리스트 안에 리스트가 있는 경우, 인덱싱을 이중으로 해주면 됩니다.

1
2
3
4
5
6
a = "안녕하세요. 테스트 문장입니다."

list_temp = [a, "hello", 3, 4, 3.141592, 'c', [1, 2, 3]]

print(list_temp[1][4])  # >>> o  (hello의 o)
print(list_temp[-1][1]) # >>> 2  ([1, 2, 3]의 2)

List 원소 여러 개 선택하기

여기서부터 저기까지 주세요!
list 에서 연속되는 원소들을 한꺼번에 반환받아야 할 때에는 슬라이싱을 사용합니다.

마치 큰 치즈를 잘라(슬라이싱) 여러 덩이로 만드는 것과 같죠.

1
2
3
4
5
6
list_temp = ["a", "b", "c", "d", "e", "f"]

b_e = list_temp[1:5]

print(b_e)
# >>> ['b', 'c', 'd', 'e']

슬라이싱에서 주의할 것은 바로 “마지막 인덱스 원소는 포함되지 않는다” 입니다.
위에서는 [1:5]를 지정했는데, 반환된 값은 4개, b부터 e까지만 반환되었죠.

이 점을 주의하시기 바랍니다.

슬라이싱에서도 음수 인덱싱을 지원하며, 이중 인덱싱도 가능하죠.

1
2
3
4
list_temp = ["a", "b", "c", "d", "e", "f"]

print(list_temp[1:-1])      # >>> ['b', 'c', 'd', 'e']
print(list_temp[1:5][0:1])  # >>> ['b', 'c']

여기까지 list 자료형의 기본적인 개념과 사용법을 알아봤습니다.
이어서 제대로 list 형을 사용하기 위한 활용법을 보도록 하겠습니다.

List 활용하기

List의 메서드 설명 예시
.append(x) 리스트의 마지막에 원소를 추가합니다.  
.insert(i, x) 지정된 인덱스에 항목을 삽입합니다.  
.extend(iterable) 리스트의 끝에 이터러블(반복 가능한, 배열과 같은)의 모든 원소를 덧붙여 확장합니다.  
리스트[i] = x 지정된 인덱스의 값을 지정한 값으로 바꿉니다.  
.remove(x) 리스트에서 특정 값을 삭제합니다.  
.pop() 리스트의 가장 마지막 원소를 제거하면서 그 값을 반환합니다.  
.pop([i]) 지정된 인덱스의 원소를 제거하면서 그 값을 반환합니다.  
del 리스트의 원소를 삭제합니다.  
.clear() 리스트의 모든 원소를 삭제합니다.  
.index(x) 리스트 내에 특정 값과 같은 원소의 인덱스를 반환합니다.  
.count(x) 리스트 내에 특정 값이 등장하는 횟수를 반환합니다.  
.sort(*) 리스트를 정렬합니다.  
.reverse() 리스트의 순서를 거꾸로 뒤집습니다.  
.copy() 리스트를 복사합니다.  

List 에 값을 추가하기

리스트에 값을 추가하는 방법은 append, insert, extend 가 있습니다.

append

먼저, 가장 기본적인 append 부터 보겠습니다.
append는 리스트의 가장 마지막에 원소를 추가하는 메서드입니다.

1
2
3
4
5
6
list_temp = [1, 2, 3, 4, 5]
list_temp.append(6)

print(list_temp)
# >>> [1, 2, 3, 4, 5, 6]
# 리스트의 가장 마지막에 6이 추가됨

insert

insert 메서드를 통해 지정한 인덱스 순서에 지정한 값을 넣을 수도 있습니다.
만약 지정한 인덱스 뒤에 원소들이 있다면, 하나씩 그 순서가 밀리게 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
--list.insert(인덱스,넣을값)--

list_temp = [1, 2, 3, 4, 5]
list_temp.insert(2, 10)

print(list_temp)
# >>> [1, 2, 10, 3, 4, 5]


--원래 리스트의 index보다  index를 지정할 경우, 가장 마지막 원소로 추가됨--

list_temp = [1, 2, 3, 4, 5]
list_temp.insert(10, 20)

print(list_temp)
# >>> [1, 2, 3, 4, 5, 20]

extend

마지막은 extend 입니다.
extend는 설명이 조금 어려울 수 있는데, 공식 doc에는 “리스트의 끝에 이터러블의 모든 원소를 덧붙여 확장한다” 라고 설명되어있습니다.

이터러블 iterable 이란, “반복할 수 있는” 이라는 뜻을 가지고 있는데요,
쉽게 리스트와 같은 형태를 떠올리시면 되겠습니다.

그래도 어렵다면, for문과 같은 반복문의 대상이 될 수 있다면, 이터러블 객체입니다.

1
2
3
4
5
6
7
list_temp = [1, 2, 3, 4, 5]
list_2 = [6, 7, 8, 9]

list_temp.extend(list_2)

print(list_temp)
# >>> [1, 2, 3, 4, 5, 6, 7, 8, 9]

“어 그러면 append나 insert 로도 넣을 수 있는 거 아냐?” 라는 의문이 드실 수도 있을 텐데요,
아래의 예시를 함께 보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# extend 를 사용할 경우
list_extend = [1, 2, 3, 4, 5]
list_2 = [6, 7, 8, 9]

list_extend.extend(list_2)

# append 혹은 insert를 사용할 경우
list_append = [1, 2, 3, 4, 5]
list_2 = [6, 7, 8, 9]

list_append.append(list_2)

# 출력
print(list_extend)
print(list_append)
# extend : [1, 2, 3, 4, 5, 6, 7, 8, 9]
# append : [1, 2, 3, 4, 5, [6, 7, 8, 9]]

둘의 결과값이 다른 걸 볼 수 있죠!
extend의 경우, list_2의 각 원소 값이 하나 하나 분해되어 들어갔다면,
append는 리스트가 통째로 list_append 에 추가되었죠.

“이터러블 객체의 모든 원소를 덧붙인다” 라는 말이 바로 이것입니다.
반복 가능한 집단 내부의 각 원소 값을 떼어서 붙인다는 말이죠.

그러면 이중 리스트를 extend 할 경우는 어떻게 될까요?

1
2
3
4
5
6
7
list_extend = [1, 2, 3, 4, 5]
list_2 = [[6, 7, 8, 9], [10, 11]]

list_extend.extend(list_2)

print(list_extend)
# >>> [1, 2, 3, 4, 5, [6, 7, 8, 9], [10, 11]]

extend 로 추가되는 대상 안에 또다시 이터러블 객체가 있다고 해도
가장 바깥 껍질의 이터러블만이 해체되어 추가됩니다. 참고!

List의 값을 바꾸기

list에 값을 추가해봤다면, 이번에는 list 안 원소를 바꿔보겠습니다.

이럴 때에는 인덱싱 혹은 슬라이싱을 사용하게 됩니다.

1
2
3
4
5
6
7
8
9
list_temp = [1, 2, 3, 4, 5]
a = 15
b = [7, 8, 9]

list_temp[0] = a
list_temp[2:-1] = b

print(list_temp)
# >>> [15, 2, 7, 8, 9, 5]

리스트의 0번째 값은 a(15)로 바뀌었고,
리스트의 [2:-1] 까지는 b([7, 8, 9]) 로 바뀌었죠.

이처럼 List 안 원소를 바꿔줄 수 있습니다.

그리고 b의 경우에 보이듯, 바꾸려고 지정한 원소의 개수가 새로이 주어진 원소의 개수보다 많은 경우,
나머지 원소는 원래 값을 유지하게 됩니다.

List 에서 값을 제거하기

list에서 원소를 제거하는 방법은 remove, pop, clear, del 이 있습니다.

remove

remove는 리스트에서 특정한 값을 삭제할 때 사용합니다.
대신, 삭제하려는 값의 “값” 자체를 정확히 알고 있어야 합니다.
이게 무슨 말인지는 아래 pop 에서 이어서 설명될 것이니, 지금은 참고만 해주세요.

1
2
3
4
5
6
7
list.remove("삭제할 값")

list_temp = ["안", "녕", "하", "세", "요"]
list_temp.remove("안")

print(list_temp)
# >>> ["녕", "하", "세", "요"]

pop

파이썬의 list 에는 pop이라는, 팝팝 튀는 개성의 메서드가 있습니다.
pop은 파라미터 없이 쓰이게 되면 리스트의 가장 마지막 원소를,
그리고 pop에 인덱스 번호를 파라미터로 주면 인덱스에 해당하는 원소를 제거하게 됩니다.

1
2
3
4
5
6
7
8
9
10
11
list_temp = ["안", "녕", "하", "세", "요"]

list_temp.pop()
print(list_temp)
# >>> ['안', '녕', '하', '세']
# 가장 마지막인 "요" 가 빠짐

list_temp.pop(1)
print(list_temp)
# >>> ['안', '하', '세']
# [1] 즉 두 번째 원소인 "녕"이 빠짐

pop은 앞서 말한 remove와는 달리, 값보다는 “특정 순서(인덱스)”를 알고 있을 때 사용하면 됩니다.
값을 알고 있다면 remove, 값을 모르고 순서를 안다면 pop.

그리고 pop은 제거한 값을 “반환” 해주는 역할도 합니다.
여러모로 다양한 활용이 가능한 메서드죠.

1
2
3
4
5
6
7
list_temp = ["안", "녕", "하", "세", "요"]

a = list_temp.pop()
b = list_temp.pop(1)

print("a : ", a, ", b : ", b)
# >>> a :  요 , b :  녕

del

del 또한 순서 즉, 인덱스를 알고 있을 경우 사용합니다.

그런데 del은 조금 특이합니다.
일반적인 파이썬 메서드와 그 사용 형태가 다르죠.

설명보다는, 예시를 들어보겠습니다.

1
2
3
4
5
6
list_temp = ["안", "녕", "하", "세", "요"]

del list_temp[1]

print(list_temp)
# >>> ['안', '하', '세', '요']

del의 경우, 메서드같은 del을 먼저 쓰고, 그 뒤에 del이 적용될 리스트와 원소의 인덱스를 적어줍니다.
여러모로 다른 python 메서드와 그 사용 형태가 다르죠.

이는 del은 메서드가 아닌, 예약어이기 때문입니다.
이와 관련해서는 잘 정리한 글들이 많으니, 찾아보시기 바랍니다. 재밌어요!

그리고 del은 여러 원소를 한꺼번에 제거할 수도 있습니다.
바로 슬라이싱을 이용해서요.

1
2
3
4
5
6
list_temp = ["안", "녕", "하", "세", "요"]

del list_temp[1:4]

print(list_temp)
# >>> ['안', '요']

clear

clear는 아주 강력합니다.
리스트 안의 원소를 모두 제거하죠.

1
2
3
4
5
list_temp = [1, 2, 3, 4, 5]
list_temp.clear()

print(list_temp)
# >>> []

[]는 리스트 객체이나, 포함하는 원소가 없을 경우 반환되는 값입니다.

기타 List의 메서드들

리스트의 선언, 원소 추가, 원소 제거에 대해 알아봤는데요,
이번에는 그 외의 메서드들을 알아보겠습니다.

index

index 는 리스트의 특정 원소가 어디에 위치해있는지, 그 순서(인덱스)를 반환하는 메서드입니다.

1
2
3
4
5
list_temp = ["안", "녕", "하", "세", "요"]

print(list_temp.index("하"))
# >>> 2
# 세 번째 위치한 (인덱스 = 2) "하"

만약 찾으려는 값이 없다면 ValueError가 발생합니다.

1
2
3
4
list_temp = ["안", "녕", "하", "세", "요"]

print(list_temp.index("다"))
# >>> ValueError: '다' is not in list

count

count 는 리스트에 특정 원소가 몇 개 포함되어있는지를 반환하는 메서드입니다.

1
2
3
4
list_temp = [1, 1, 1, 2, 3, 4, 4, 5]

print(list_temp.count(1))   # >>> 3 (1은 3개가 포함됨)
print(list_temp.count(4))   # >>> 2 (4는 2개가 포함됨)

sort

sort는 리스트를 정렬해주는 메서드입니다.
reverse 옵션을 통해 오름차순으로 정렬할지, 내림차순으로 정렬할지 정할 수 있습니다.

주의할 점. sort() 메서드는 특정 값을 반환해주지 않고, “정렬해주는” 기능만을 수행합니다.
그리고 sort를 만난 리스트는 정렬된채로 바뀌어 고정되죠.
아래 예시를 보겠습니다.

1
2
3
4
5
6
7
list_temp = ["마", "라", "다", "나", "가"]

print(list_temp)            # >>> ['마', '라', '다', '나', '가']
print(list_temp.sort())     # >>> None
print(list_temp)            # >>> ['녕', '세', '안', '요', '하']

# .sort()는 "정렬해주는 기능" 만을 가질 뿐, 반환하는 값이 없음

앞서 말했듯, reverse 옵션을 통해 정렬의 방법을 정할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
list_temp = [5, 4, 3, 2, 1]

list_temp.sort(reverse=False)
print(list_temp)
# >>> [1, 2, 3, 4, 5]
# 오름차순 정렬

list_temp.sort(reverse=True)
print(list_temp)
# >>> [5, 4, 3, 2, 1]
# 내림차순 정렬

reverse

reverse 는 리스트 원소의 순서를 180도 뒤집는 메서드입니다. sort와 마찬가지로 “뒤집는 기능”을 할 뿐, 반환하는 값은 없습니다.

1
2
3
4
list_temp = ["안", "녕", "하", "세", "요"]

print(list_temp.reverse())  # >>> None
print(list_temp)            # >>> ['요', '세', '하', '녕', '안']

copy

copy는 리스트를 복사하는 메서드입니다.

1
2
3
4
5
6
7
list_a = ["안", "녕", "하", "세", "요"]
list_b = list_a.copy()

print("a : ", list_a)
print("b : ", list_b)
# >>> a : ['안', '녕', '하', '세', '요']
# >>> b : ['안', '녕', '하', '세', '요']

그리고 copy 된 리스트는 원래의 리스트와 다른 주소값을 가지게 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
list_a = ["안", "녕", "하", "세", "요"]
list_b = list_a.copy()

list_a[2] = "★"    # list_a 의 "하" 를 "★"로 바꾸면

print(list_a)       # >>> ['안', '녕', '호', '세', '요']
print(list_b)       # >>> ['안', '녕', '★', '세', '요']

# copy된 경우, 복사된 원소는 원래의 원소와 다른 주소값을 가진다.  
print(id(list_a))   # >>> 4466927040
print(id(list_b))   # >>> 4467721472

# copy가 아닌, =으로 같은 주소값을 가리키게만 하면, 동일한 주소값
list_c = list_a
print(id(list_a))   # >>> 4466927040
print(id(list_c))   # >>> 4466927040

min, max

min 과 max 는 각각 리스트 내의 최소값, 최대값을 찾아주는 메서드입니다.
리스트와 관련된 메서드라기보다는 이터러블 객체에 적용할 수 있는 파이썬 내장 기본 함수이나,
리스트와 함께 자주 사용되기 때문에 소개합니다.

1
2
3
4
5
6
7
list_temp = [1, 2, 3, 4, 5, 6, 7]

m = min(list_temp)
M = max(list_temp)

print(f'list_temp내 원소의 최대값은 {M}, 최소값은 {m}')
# >>> list_temp내 원소의 최대값은 7, 최소값은 1

심화

리스트 자료형은 앞으로 파이썬을 하면서 수도 없이 만나게 될 겁니다.
그리고 정말 편리하게 사용할 수 있는 고마운 자료형이라는 생각도 들게 됩니다.

아래 두 가지 심화 활용으로 이번 포스팅을 마치겠습니다.

for 문과 함께 사용하기

1
2
3
4
5
6
7
8
9
list_temp = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = []

# 짝수 찾기
for i in list_temp:
    if i % 2 == 0 : result.append(i)

print(result)
# >>> [2, 4, 6, 8, 10]
1
2
3
4
5
6
7
8
9
10
list_temp = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = []

# 값 뒤섞기
for i, j in enumerate(list_temp):
    a = list_temp.pop(10-j)
    list_temp.insert(j, a)

print(list_temp)
# >>> [10, 1, 2, 8, 4, 6, 7, 5, 9, 3]

list comprehension

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
text = 'stop words는 금지 단어 리스트를 의미합니다.'
stop_words = ['금지', '단어', '리스트']

# (1) 반복문 활용
# text에 포함된 단어를 원소로 가지는 리스트 만들기

text_list = [x for x in text.split()]
# >>> 출력 : ['stop', 'words는', '금지', '단어', '리스트를', '의미합니다.']"


# (2) 반복문과 조건문 함께 활용
# stop words에 포함되지 않는 단어로 리스트 만들기

clean_words = [x for x in text_list if x not in stop_words]
# >>> 출력 : ['stop', 'words는', '리스트를', '의미합니다.']"

Reference

Python List Doc : https://docs.python.org/ko/3/tutorial/datastructures.html#using-lists-as-queues
리스트 인덱싱, 슬라이싱 : https://wikidocs.net/16037
이터러블 iterable : https://ko.javascript.info/iterable
del 예약어 : https://ooyoung.tistory.com/49
id() 주소값 : https://technote.kr/289
min, max : https://devpouch.tistory.com/71