Tips/Python

[Py린이를 위한 Python 기초] 5. 리스트 응용

엘사박 2023. 2. 22. 16:52

※ 이 글은 책 「파이썬 코딩도장」의 내용을 재정리한 내용임

- 파이썬 시리즈 글 순서->  링크

 

 

- 이 글이 사실 제일 쓰고 싶었다

- 시퀀스 객체 중 하나인 리스트에 대해 조금 더 자세히 살펴보도록 하자

 

 

 

 

 

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 라이브러리에서 또 다른 객체들을 배울 때 리스트와 유사한 객체가 등장하니 잘 숙지하면 좋을 것 같다!