[Py린이를 위한 Python 기초] 5. 리스트 응용
※ 이 글은 책 「파이썬 코딩도장」의 내용을 재정리한 내용임
- 파이썬 시리즈 글 순서-> 링크
- 이 글이 사실 제일 쓰고 싶었다
- 시퀀스 객체 중 하나인 리스트에 대해 조금 더 자세히 살펴보도록 하자
1) 리스트 다시 정리
● 리스트란?
- 편집 가능하고 순서가 있는 객체 묶음
- 기본 형태는 = [값1, 값2, 값3]
- 여기서 들어가는 값은 문자열일 수도, 정수일 수도, 실수일수도 있음
● 만드는 방법?
- 값들의 묶음을 대괄호로 묶어주는 방법
#형태 1
변수 = [값1, 값2, 값3...]
#형태 1 예시
a = [1,'사과', 3.5]
print(a)
#[1,'사과', 3.5]
- list() 함수를 쓰는 방법
#형태 2
변수 = list(값1, 값2, 값3)
# 형태 2 예시
b = list(3, '레몬', 5.5)
print(b)
#[3, '레몬', 5.5]
- 일일이 숫자를 쓸 필요없이 range()함수를 이용하는 방법
#형태 3
변수 = list(range(시작, 끝, 증가폭))
#형태 3 예시
c = list(range(10, 5, -1))
print(c)
# [10, 9, 8, 7, 6]
- 문자열 자체에 list() 함수를 씌우면 글자 하나하나 떨어지기도 함
#형태 4
list(문자열) =['문', '자', '열']
#형태 4 예시
d = 'hello'
e = list(d)
print(e)
#['h', 'e', 'l', 'l', 'o']
● 빈 리스트
- 리스트 대괄호 안에 아무 객체도 들어있지 않은 리스트를 빈 리스트라고 함
- 변수 = [ ] 로 빈리스트를 선언할 수 있음
a = []
print(type(a), a)
# <class 'list'> []
2) 리스트 슬라이싱
● 시퀀스 인덱스
- a = [10, 5, '파이썬', 8, 10] 이라는 리스트가 있을 때, 세번째 값을 가져오고 싶다
- a[인덱스]으로 가져올 수 있다
a = [10, 5, '파이썬', 8, 10]
a[3]
# 8
- 세 번째 값을 가져오려고 a[3]을 했더니 4번째 값인 8을 가져왔다
- 파이썬 인덱스는 1이 아니라 0부터 시작하기 때문임
- 따라서 내가 10번째 인덱스에 해당하는 값을 가져오려 한다면 10-1 = 9를 괄호안에 넣어야함
a = [10, 5, '파이썬', 8, 10]
a[2]
# '파이썬'
- a[-1]을 하면 인덱스의 끝이 어디든 마지막 인덱스의 값을 들고 온다.
a = list(range(100))
print(a[-1])
b = list(range(1000))
print(b[-1])
# 99
# 999
- 같은 이치로 a[-2]는 뒤에서 두번째 값, a[-3]은 뒤에서 세번째 값을 들고 온다.
● 시퀀스 슬라이스
- 위의 시퀀스 인덱스를 응용하면 리스트를 조각내어 들고 올 수 있다
- a[시작인덱스:끝인덱스:증가폭]
- range()함수와 비슷하지만 range(시작, 끝, 증가폭)는 쉼표로 구분되고 시퀀스 슬라이스는 콜론으로 구분됨
- 예를 들면 다음과 같다
- 1~10까지의 수가 들어있는 리스트에서 [3,6,9]만 조각내어 들고 오고 싶다
- 슬라이스를 어떻게 작성해야할까?
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
a[2:9:3]
#[3, 6, 9]
- 값은 1~10까지이지만 인덱스는 0~9까지임
- 3부터 시작해야하니 시작인덱스는 2가 됨
- 3, 6, 9로 증가폭은 3임
- 마지막 값이 9이고 값9의 인덱스는 8임
- 하지만 끝인덱스를 8로 적으면 [3,6]까지만 들고옴
- 슬라이스 인덱스에서 끝인덱스에 해당하는 값은 들고오지 않기 때문임
- 따라서 끝인덱스는 내가 들고오고자 하는 인덱스보다 +1한 숫자를 넣어야함(여기선 9)
- 그러므로 a[2:9:3] 이라고 써야 [3,6,9]를 들고 올 수 있음
3) 리스트 메소드
- 여기서는 편의상 리스트 a = [1, 2, 3.3]을 선언하고 관련된 메소드에 대해 알아보도록 하겠다
- 후루룩 읽어보고 따라해보기.. 헷갈릴 때 와서 보기 좋도록 표로 만들어 놓음
메소드 | 설명 | 예시 | |
추가 | a.append() | 리스트 끝에 요소 하나를 추가 | a = [1, 2, 3.3] a.append('엘사박') print(a) #[1 ,2, 3.3, '엘사박'] |
a[len(a): ] = 추가할 리스트 | 리스트 끝에 리스트를 추가 | a = [1, 2, 3.3] a[len(a): ] = [4, 5] print(a) #[1, 2, 3.3, 4, 5] |
|
a.insert(인덱스, 추가요소) | 특정 인덱스에 요소 추가 | a = [1, 2, 3.3] a.insert(2,1000) print(a) #[1, 2, 1000, 3.3] |
|
삭제 | a.pop() | 마지막 요소 삭제 후 삭제값 반환 | a = [1, 2, 3.3] a.pop() # 3.3 |
a.pop(인덱스) | 인덱스 해당값 삭제 후 삭제값 반환 | a = [1, 2, 3.3] a.pop(1) # 2 |
|
del a | 리스트 삭제(변수 자체를 삭제) | a = [1, 2, 3.3] del a print(a) #NameError |
|
a.clear() | 리스트 안 모든 요소 삭제, 빈리스트로 만듦 | a = [1, 2, 3.3] a.clear() print(a) # [ ] |
|
a.remove(값) | 원하는 값을 찾아 삭제, 같은 값이 여러개일 경우 처음 찾은 값만 삭제 | a = [1, 2, 3.3, 3.3, 3.3] a.remove(3.3) print(a) # [1, 2, 3.3, 3.3] |
|
정렬 | a.reverse() | 리스트 순서 뒤집기 | a = [1, 2, 3.3] a.reverse() print(a) # [3.3, 2, 1] |
a.sort() 또는 a.sort(reverse=False) |
오름차순 정렬 | a = [3.3, 1, 2] a.sort() print(a) # [1, 2, 3.3] |
|
a.sort(reverse=True) | 내림차순 정렬 | a = [3.3, 1, 2] a.sort(reverse=True) print(a) # [3.3, 2, 1] |
|
sorted(a) | 정렬한 새 리스트 생성(원본은 그대로 유지) | a = [3.3, 1, 2] sorted(a) print(a) # [3.3, 1, 2] |
|
복사 | 변수.copy(a) | 리스트 복사 | a = [3.3, 1, 2] b. copy(a) print(b) # [3.3, 1, 2] |
변수 = copy.deepcopy(a) | 2차원 리스트(다음 파트에서 설명)에 대해 복사 | import copy a = [[''사과', '레몬'], ['수박', '감자']] b = copy.deepcopy(a) print(b[0][1]) # 레몬 b[0][1] = '엘사박' print(b) #[['사과', '엘사박'], ['수박', '감자]] |
|
기초통계량 | min(a) | 리스트 값 중 최소값 찾아 반환 | a = [3.3, 1, 2] min(a) # 1 |
max(a) | 리스트 값 중 최대값 찾아 반환 | a = [3.3, 1, 2] max(a) # 3.3 |
|
sum(a) | 리스트 값 합계 반환 | a = [3.3, 1, 2] sum(a) # 6.3 |
|
a.count(값) | 해당 값 개수 찾기 | a = [3.3, 1, 2] a.count(3.3) # 1 |
★ '복사' 카테고리의 copy.deepcopy(a) 예시가 괴랄하다고 느낄 수 있으나 바로 밑에서 설명하고 있으니 찬찬히 읽기
★ 기타 메소드는 구글링 하시라(파이썬 리스트 인덱스 추출 등...으로 검색하면 잘 나올 것)
4) 2차원 리스트
● 2차원 리스트란?
- 방금까지 보던 대괄호 한 쌍의 리스트를 우리는 1차원 리스트라 부른다
- 그렇다면 2차원 리스트는 대괄호가 2쌍이라는 걸까? 맞다 예시는 [[1, 2], [3, 4, 5], [6,7,8]] 가 있다.
- 3차원 리스트도 있는가? 10차원까지도 만들 수 있다. 대괄호를 얼마나 쓰냐에 달린문제이다.
● 다차원 리스트의 인덱스
- 다차원 리스트의 특정 인덱스 값을 불러오려면 대괄호 껍질 하나하나에 대해 지정해주어야한다.
- 설명하기에 앞서 a = [ [ [1, 2, 3], [4, 5, 6] ], [ [7, 8, 9], [10, 11, 12] ] ]라는 3차원 리스트를 준비해보자
- 나는 a에서 8을 꺼내고 싶다. 제일 바깥 대괄호는 신경쓰지 않아도 된다
- 8이 속해있는 묶음은 두번째로 바깥인 대괄호 중 2번째(인덱스로는 1)에 위치한다.
- a = [ [ [1, 2, 3], [4, 5, 6] ], [ [7, 8, 9], [10, 11, 12] ] ] => 따라서 a[1]
- 그다음 a[1] 중에서는 첫 번째(인덱스로는 0)에 위치한다
- a = [ [ [1, 2, 3], [4, 5, 6] ], [ [7, 8, 9], [10, 11, 12] ] ] => 따라서 a[1][0]
- a[1][0] 중에서는 두번째 값(인덱스로는 1)이다.
- a = [ [ [1, 2, 3], [4, 5, 6] ], [ [7, 8, 9], [10, 11, 12] ] ] => 따라서 a[1][0][1]
- 코드로 보면
a = [ [ [1, 2, 3], [4, 5, 6] ], [ [7, 8, 9], [10, 11, 12] ] ]
a[1][0][1]
# 8
- for 문을 이용하여 해당 리스트의 값을 하나씩 출력한다고 하면
a = [ [ [1, 2, 3], [4, 5, 6] ], [ [7, 8, 9], [10, 11, 12] ] ]
for i in a:
for j in i:
for k in j:
print(k)
# 1 (a[0][0][0])
# 2 (a[0][0][1])
# 3 (a[0][0][2])
# 4 (a[0][1][0])
# 5 (a[0][1][1])
# 6 (a[0][1][2])
# 7 (a[1][0][0])
# 8 (a[1][0][1])
# 9 (a[1][0][2])
# 10 (a[1][1][0])
# 11 (a[1][1][1])
# 12 (a[1][1][2])
- 숫자 옆에 괄호안의 인덱스는 필자가 임의로 적어놓은 것으로 print(k)만 해서는 나오지 않는다.
- 위 코드에 i는 두번째 바깥 대괄호, j는 세번째 바깥 괄호, k는 묶음 안에서 값의 순서를 뜻함.
- 위 코드에서 k = a[0][0][0] 부터 k = a[1][1][2]까지 반복하며 값을 찍어내는 것.
- 다른 반복문으로는
a = [ [ [1, 2, 3], [4, 5, 6] ], [ [7, 8, 9], [10, 11, 12] ] ]
for i in range(len(a)):
for j in range(len(a[i])):
for k in range(len(a[i][j])):
val = a[i][j][k]
print(val)
print(f'a[{i}][{j}][{k}]') #f-string
# 1, a[0][0][0]
# 2, a[0][0][1]
# 3, a[0][0][2]
# 4, a[0][1][0]
# 5, a[0][1][1]
# 6, a[0][1][2]
# 7, a[1][0][0]
# 8, a[1][0][1]
# 9, a[1][0][2]
# 10, a[1][1][0]
# 11, a[1][1][1]
# 12, a[1][1][2]
- 위 코드는 인덱스가 몇개까지 있는지 len()함수로 알아낸 다음 그 길이만큼 반복하며 인덱스를 추출해서 값을 찾아 출력하는 것이다.
- print(f'a[{i}][{j}][{k}]')는 f-string 포매팅 방법을 사용하여 출력될 값을 자동으로 바꿔가며 출력함(관련 설명은 링크 참고)
5) copy와 deepcopy의 차이점
● copy()
- 어떤 변수에 리스트를 할당한 후 그 리스트를 복사하고 싶을 때
- 예를 들어 a = [1, 2, 3]이라는 변수를 선언하고 b = a 라고 한 후 b의 값을 바꾸면 a 값은 어떻게 될까?
a = [1, 2, 3]
b = a
b[0] = 10
print(b)
print(a)
# [10, 2, 3]
# [10, 2, 3]
- a와 b는 같은 리스트(객체)를 가리키기 때문에 b의 값을 바꾸면 a값도 바뀌게 됨
- 달리 말하면 아래와 같음(값도 같고, 객체도 같다)
a = [1, 2, 3]
b = a
print(a == b)
print(a is b)
# True
# True
- 값은 똑같지만 서로 다른 '복사본'을 만들고 싶다면 위처럼 변수자체를 새로운 변수에 할당하면 안된다.
- 이럴 때 쓰는 것이 copy()함수.
- 예시는 다음과 같다
a = [1, 2, 3]
b = a.copy()
b[0] = 10
print(b)
print(a)
# [10, 2, 3]
# [1, 2, 3]
- 복사본이 잘 만들어졌다.
- 두 변수를 비교하면 어떨까
a = [1, 2, 3]
b = a.copy()
print(a == b)
print(a is b)
# True
# False
- 값은 같지만 서로다른 객체라고 한다
- 앞으로 복사를 할 땐 꼭 copy()함수를 쓰자
● deepcopy()
- 복사할 때는 copy()를 써야하는건 알겠는데 비스무리하게 생긴 함수가 또 있다
- deepcopy()? 이건 또 뭘까.
- copy()를 2차원 리스트에 쓰면 제대로 복사가 안된다.
a = [[1, 2], [3, 4]]
b = a.copy()
b[0][0] = 999
print(b)
print(a)
# [[999, 2], [3, 4]]
# [[999, 2], [3, 4]]
- 분명 카피 함수를 썼는데도 불구하고 b값을 바꾸니 a의 값도 바뀐다
- 이 때 쓰는 것이 deepcopy()
- deepcopy()는 copy모듈을 임포트 해서 써야한다
import copy
a = [[1, 2], [3, 4]]
b = copy.deepcopy(a)
b[0][0] = 999
print(b)
print(a)
#[[999, 2], [3, 4]]
#[[1, 2], [3, 4]]
6) 리스트 표현식
● 리스트 표현식이란?
- 반복문을 써서 리스트를 만들 때 조금 더 간편하게(한줄로 뚝딱) 만들 수 있도록 해놓은 것
- 기본 형태
[식 for 변수 in 리스트]
list(식 for 변수 in 리스트)
● 잘모르겠다면 예시를 보자
a = [i for i in range(10)]
print(a)
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
- 풀이 하면
- a = [ i for i in range(10)] for 구문으로 0~9까지의 i를 생성하고
- a = [i for i in range(10)] 생성한 i를 리스트에 저장하겠다는 것이다
- 여기에 if 조건문을 쓸 수도 있다
● if 조건문 쓰기
- 기본형태
[식 for 변수 in 리스트 if 조건식]
list(식 for 변수 in 리스트 if 조건식)
a = [i + 5 for i in range(10) if i % 2 == 1]
print(a)
# [6, 8, 10, 12, 14]
- 풀이를 해보자면
- a = [i + 5 for i in range(10) if i % 2 == 1] 에서
- a = [i + 5 for i in range(10) if i % 2 == 1] 0~9까지의 i 중에
- a = [i + 5 for i in range(10) if i % 2 == 1] 2로 나눴을 때 1이 남는 수만(홀수만) 가져와서
- a = [i + 5 for i in range(10) if i % 2 == 1] i에 5를 더한 값을 리스트에 저장한다는 뜻이다
- a = [i + 5 (for i in range(10) if i % 2 == 1)] i+5를 뒤에 for구문이 수식해준다..?라고 이해하면 될듯
● for문을 중복해서 쓴 예시도 보자
a = [i * j for i in range(2, 5) for j in range(1, 5)]
print(a)
# [2, 4, 6, 8, 3, 6, 9, 12, 4, 8, 12, 16]
- 풀이를 하면
- a = [i * j for i in range(2, 5) for j in range(1, 5)]에서 for문이 2번 등장하는데
- a = [i * j for i in range(2, 5) for j in range(1, 5)] (j는 2, 3, 4까지 만들어짐)
- a = [i * j for i in range(2, 5) for j in range(1, 5)] (i 가 1, 2, 3, 4까지 만들어짐)
- a = [i * j for j in range(2, 5) for i in range(1, 5)] i와 j 값을 곱해 리스트에 저장한다
- 2*1, 2*2, 2*3, 2*4 / 3*1, 3*2, 3*3, 3*4 / 4*1, 4*2, 4*3, 4*4
- 2, 4, 6, 8 / 3, 6, 9, 12 / 4, 8, 12, 16
- 이 코드는 아래와 같이 풀어서 쓸 수 있다
a = []
for i in range(2,5):
for j in range(1,5):
a.append(i*j)
print(a)
# [2, 4, 6, 8, 3, 6, 9, 12, 4, 8, 12, 16]
- 리스트에 대한 간략한 설명은 여기서 마치도록 하겠다
- 후에 Numpy 라이브러리, Pandas 라이브러리에서 또 다른 객체들을 배울 때 리스트와 유사한 객체가 등장하니 잘 숙지하면 좋을 것 같다!