Python

[파이썬 스킬 업] 3장. 리스트 기능

patrick-star 2023. 4. 27. 23:24
728x90

3.1 리스트 생성 및 활용

간단하게 왼쪽에 변수 오른쪽에 리스트라고 생각하면 된다.

myList = [1, 4, 6, 2, 44, 20] 
beatles_list= ['John', 'Paul', 'George', 'Ringo']  
  • 항목 추가 (뒤에서부터 추가) : append
my_list = []
my_list.append(10)
my_list.append(220)
my_list.append(3)   

my_list
# 출력값 : [10, 220, 3]
# 코드 순서처럼 10, 220, 3 순서로 데이터가 저장된 걸 확인할 수 있음
  • 항목 삭제
my_list = [10, 220, 3]
my_list.remove(10) # 값이 10인 항목을 삭제한다.  
                   # 중복되었다면 가장 앞에 있는 10을 삭제한다.

3.3 인덱스

문자열에서 다룬 인덱스랑 동일하다.

양수는 0부터 시작해서 마지막까지 / 음수는 맨 마지막 -1부터 시작해서 맨 처음까지

cf) 리스트 내부의 값을 확인하기 위해 여러 번 print 함수를 호출하는 경우가 많다.
하지만, 반복적으로 print 함수를 호출하면 프로그램 속도가 저하되기 때문에 다음과 같은 방법을 추천한다.

print(myList[0], myList[1], myList[2], sep = '\n')
  • enumerate 메소드

코드를 작성할 때 range 함수를 웬만하면 사용하지 않는 것이 파이썬스러운(pythonic) 방법이다.

예를 들어 이 방법보다는

for i in range(len(a_list)):
    print(a_list[i])

아래 방법을 쓰는게 더 좋다.

a_list = ['Tom', 'Fab', 'Jeong']

for s in a_list :
    print(s) 

여기서 각 항목을 숫자와 함께 출력하고 싶을 때 enumerate 함수를 사용할 수 있다.
항목을 숫자와 함께 출력한다는 건 이런 거다.

(1, 항목1), (2, 항목2), (3, 항목3), ... 

enumerate를 사용해서 숫자와 항목을 함께 출력해보자.

a_list = ['zoom', 'two', 'pay']

list(enumerate(a_list, 1)) # 1부터 시작해서 항목을 나열
# 출력 : [(1, 'zoom'), (2, 'two'), (3, 'pay')]

list(enumerate(a_list, 4)) # 4부터 시작해서 항목을 나열
# 출력 : [(4, 'zoom'), (5, 'two'), (6, 'pay')]

enumerate 함수의 출력 형식이 (숫자, 리스트 항목) 이기 때문에 다음과 같이 for문을 작성할 수 있다.

for (item_num, name_str) in enumerate(a_list, 1):
   print(item_num, '. ', name_str, sep = '') 

1. zoom
2. two
3. pay

3.5 슬라이스 안에 값 대입

my_list = [10, 20, 30, 40, 50, 60] 

my_list[1:4] = [707, 200] # my_list의 1,2,3번째 값([20, 30, 40])을 [707, 200]으로 변경한다. (참고로 리스트는 0번째 부터 시작) 

my_list ==> [10, 707, 200, 50, 60]

슬라이싱 범위의 길이가 0인 인덱스를 넣을 수도 있다.
이러면 기존 값을 삭제하지 않고 해당 위치에 새로운 리스트 항목을 삽입할 수 있다.

my_list = [10, 707, 200, 50, 60]

my_list[0:0] = [12, 33] # 0번째 위치에 12, 33을 삽입합

my_list ==> [12, 33, 10, 707, 200, 50, 60]

my_list[2:2] = [8,9,0] # 2번째 위치에 8,9,0을 삽입함

my_list ==> [12, 33, 8, 9, 0, 10, 707, 200, 50, 60]

단, 다음과 같은 제약 사항이 있다.

슬라이싱 안에 리스트를 대입할 때 ...

1) 대입 대상은 항목이 전혀 없거나 하나만 있더라도 반드시 다른 리스트나 컬렉션이어야 한다
2) 스텝이 명시되었다면 슬라이싱의 범위와 삽입할 데이터의 길이가 반드시 같아야 한다.

3.6 리스트 내장 연산

연산자/문법 설명
리스트1 + 리스트2 이어붙이기. 리스트1과 리스트2의 모든 항목이 포함된 새로운 리스트를 생성
리스트 * n 또는 n * 리스트 리스트의 항목을 n번 반복한 리스트를 생성 ex) [0] * 3 = [0. 0. 0]
리스트1 = 리스트2 얕은복사
리스트1 = 리스트2[:] 깊은복사
리스트1 == 리스트2 / 리스트1 != 리스트2 두 리스트의 모든 항목의 내용이 같은지/다른지 파악
항목 in 리스트 / 항목 not in 리스트 리스트 안에 항목이 있는지/없는지 파악
리스트1 < / <= / > / >= 리스트2 항목 간 비교 연산
*리스트 리스트를 unpacked해서 나열한 독립적인 각각의 항목들로 대체된다.
  • * 연산자
[0] * 1000 ==> 0이 1000개 있는 리스트를 만들어낼 수 있다. 
  • 깊은 복사

위에서 얘기한 깊은 복사를 할 때 사용하는 연산자는 다음과 같은 경우에 문제가 생길 수 있다.

a_list = [1, 2, [5, 10]]
b_list = a_list[:] 

b_list[2][0] = 0
b_list[2][1] = 0 

a_list : [1, 2, [0, 0]]

분명 깊은 복사를 했다고 생각했지만 그렇지 않다는 걸 확인할 수 있다.
왜냐하면, b_list가 a_list 자체를 깊은 복사한 건 맞지만 a_list 안에 있는 [5, 10] 까지 깊은 복사를 한 건 아니기 때문이다.

즉, 내부에 있던 [5. 10] 리스트는 얕은복사가 이뤄진 것이다.

그래서 copy 모듈을 가져와서 deepcopy 메소드를 사용하는 걸 추천한다.

import copy

a_list = [1, 2, [5, 10]]
b_list = copy.deepcopy(a_list) 

3.8 리스트 함수

파이썬의 기본 함수 중 리스트와 함께 사용할 수 있는 함수를 살펴본다.

함수 설명
len(컬렉션) 컬렉션 길이 반환
max(컬렉션) 최댓값 항목 반환
min(컬렉션) 최솟값 항목 반환
reversed(컬렉션) 역순으로 정렬된 iterator 반환
sorted(컬렉션) 정렬된 리스트 생성
sum(컬렉션) 모든 항목을 더한 값 반환(단, 항목들이 모두 숫자여야 함)

3.9 리스트 메서드: 리스트 수정

| 함수 | 설명 |
| 리스트.append(값) | 항목 추가 |
| 리스트.clear() | 모든 항목 제거 |
| 리스트.extend(이터러블) | 하위 리스트 추가 |
| 리스트.insert(인덱스, 값) | 인덱스 위치에 값 삽입 |
| 리스트.remove(값) | 값의 첫 인스턴스 제거 (동일한 값이 있다면 가장 앞에 있는 값을 제거) |

  • append, extend
a_list = [1, 2, 3] 

# 같은 결과를 반환한다. 
# append는 하나의 값을 삽입하고
# extend는 컬렉션이나 이터러블을 매개변수로 받고 추가한다. 
a_list.append(55) 
a_list.extend([55]) 

# 리스트에 3개의 항목을 추가한다
a_list.extend([55, 66, 77]) 
  • remove

제거할 값이 리스트에 없다면 ValueError 예외가 발생함

3.10 리스트 메서드: 정보 가져오기

함수 설명
리스트.count(값) 매개변수로 전달한 값의 개수 반환
리스트.index(값[, 시작 [, 종료]] 값의 인덱스 반환(값이 여러 개 있다면 가장 앞에 있는 값의 인덱스를 반환)
리스트.pop([인덱스]) 인덱스의 값 반환 및 제거 (default로 마지막 인덱스)
  • count
yr_list = [1, 2, 1, 1, [3, 4]] 

yr_list.count(1) # 1의 개수 3개  
yr_list.count(2) # 2의 개수 1개  
yr_list.count(3) # 3의 개수 0개  
yr_list.count(\[3, 4\]) # \[3, 4\]의 개수 1개

3.11 리스트 메서드: 정렬

함수 설명
리스트.sort([key = None], [, reverse = False]) reverse = True로 설정하면 역순으로 정렬됨
리스트.reverse() 현재 순서를 뒤집는다 (정렬이 아니라 순서만 뒤집는다)

두 메서드를 사용하려면 리스트의 모든 항목을 서로 비교할 수 있어야 한다. 전부 문자열이거나 전부 숫자여야 한다.

  • sort 메소드의 매개변수 key
def ignore_case(s) :
    return s.casefold() # 모든 문자를 소문자로 바꾸는 메소드 

a_list = ['john', 'paul', 'George', 'brian', 'Ringo'] 
b_list = a_list[:]

a_list.sort() 
b_list.sort(key = ignore_case) 

a_list와 b_list의 정렬 결과는 다음과 같이 다른 결과가 나타난다.

>>> a_list
['George', 'Ringo', 'brian', 'john', 'paul']
>>> b_list
['brian', 'George', 'john', 'paul', 'Ringo']

이렇게 결과가 달라진 이유는 b_list를 정렬할 때 key 값으로 위에서 정의한 ignore_case를 전달함으로써

b_list의 모든 값들은 ignore_case 메소드에 의해 값이 변경된다.

즉, b_list.sort(key = ignore_case)를 통해 정렬 대상이 되는 리스트는 다음과 같이 변경된다.

b_list = ['john', 'paul', 'George', 'brian', 'Ringo'] 

==> b_list = ['john', 'paul', 'george', 'brian', 'ringo'] 
==> 이렇게 변경된 리스트를 정렬하기 때문에 a_list의 결과와 차이가 생긴다. 

3.13 reduce 함수

파이썬 리스트에서는 사용자가 직접 리스트의 모든 항목을 한 번에 처리할 수 있는 함수를 정의할 수 있다.
map, filter와 같은 리스트 메서드가 위와 같은 기능을 제공한다.

  • map : 주어진 리스트의 모든 항목을 변환해서 새로운 리스트를 생성
  • filter : 구체적인 조건을 만족하는 항목들로 구성된 새로운 리스트를 생성

functools 패키지의 여러 함수를 사용하면 리스트를 다양한 방식으로 처리할 수 있다.
물론 import functools를 사용해줘야 한다.

그 중 functools.reduce 메소드를 살펴보자. 간단한 예시와 함께 사용법을 알아보자.

import functools 

def add_func(a, b) : 
    return a+b

def mul_func(a, b) : 
    return a*b

n = 5
a_list = list(range(1, n+1)) ==> a_list = [1, 2, 3, 4, 5] 

functools.reduce(add_func, a_list) # 1 + 2 + 3 + 4 + 5
functools.reduce(mul_func, a_list) # 1 * 2 * 3 * 4 * 5

functools.reduce(add_func, a_list)를 보면

  • 1, 2가 functools.reduce(add_func, a_list)의 매개변수가 된다. 이때, 결과값은 3이 된다.
  • 3, 3이 functools.reduce(add_func, a_list)의 매개변수가 된다. 이때, 결과값은 6이 된다.
  • 6, 4가 functools.reduce(add_func, a_list)의 매개변수가 된다. 이때, 결과값은 10이 된다.
  • 10, 5가 functools.reduce(add_func, a_list)의 매개변수가 된다. 이때, 결과값은 15이 된다.

이런식으로 동작하도록 하는 게 reduce 메서드이다.

3.14 람다식

앞서 reduce 함수를 사용할 때 add_func, mul_func와 같이 간단한 함수조차 일일이 정의해줘야 하는 불편함이 있었다.

이러한 불편함을 람다식을 통해 해결할 수 있다.

functools.reduce(lambda x, y : x+y, [1,2,3,4,5]) 

functools.reduce(lambda x, y : x*y, [1,2,3,4,5])  

3.13에서의 예시와 동일한 동작을 한다. 이전처럼 함수를 별도로 정의하지 않아도 된다.

3.15 List Comprehension(리스트 함축)

리스트 함축이 하는 일을 간단하게 말하면 모든 항목을 대상으로 항목 간(member by member) 복사를 수행하는 것이다.

항목 간 복사는 아래와 같이 여러가지로 구현할 수 있다.

b_list = a_list[:] 

b_list = [] 
for i in a_list : 
    b_list.append(i) 

위와 같은 방식을 리스트 함축을 이용해서 간단하게 구현할 수 있다.

b_list = [i for i in a_list] 

# a_list의 제곱 값을 담고 싶다면 
b_list = [i*i for i in a_list] 

중첩 for문도 구현할 수 있다.

mult_list = [i * j for i in range(3) for j in range(3)] 

# 위/아래는 둘 다 동일한 동작을 한다. 
for i in range(3) : 
    for j in range(3) : 
        mult_list.append(i * j)

if문을 추가할 수도 있다.

new_list = [i for i in my_list if i > 0] 

my_list에 있는 항목 중에서 0보다 큰 항목을 가지고 새로운 리스트를 생성했다.

3.16 딕셔너리와 세트(set)의 함축(comprehension)

list comprehension은 세트와 딕셔너리까지 확장된다.

Set

ex) a_list의 양수 값만 선택해서 set에 저장하는 코드

my_set = set() 

for i in a_list : 
    if i > 0 : 
        my_set.add(i)

# comprehension으로 표현하면 
my_set = {i for i in a_list if i > 0} # 이와 같이 간단하게 표현할 수 있다

3.15와 달리 [] 대신에 {}를 사용함으로써 set가 생성되었다는 걸 확인할 수 있다.

ex) a_list의 양수 값의 제곱 값을 set에 저장하는 코드

# comprehension으로 표현하면 
my_set = {i * i for i in a_list if i > 0} # 이와 같이 간단하게 표현할 수 있다

Dictionary

쉬운 예시로 살펴보자.

vals_list = [ ('pi', 3.14), ('phi', 1.618) ] 

my_dict = { i[0] : i[1] for i in vals_list } # vals_list라는 튜플들을 가진 리스트를 딕셔너리 형태로 저장한다.
  • 두 리스트를 하나의 딕셔너리로 합침

    keys = ['Bob', 'Carol', 'Ted', 'Alice'] 
    vals = [4.0, 4.0, 3.75, 3.9] 

grade_dict = {keys[i] : vals[i] for i in range(len(keys)) }

또는

grade\_dict = {key:val for key, val in zip(keys, vals)} # 성능을 개선한 방법

이 코드를 실행하면


grade\_dict = {'Bob':4.0, 'Carol':4.0, 'Ted':3.75, 'Alice':3.9}

라는 딕셔너리가 생성된다.

  • 많이 사용하는 예시 - key, value 값을 바꾸기

idict = { v:k for k, v in phone_dict.items() }


딕셔너리의 `items()` 메소드를 통해 키(k), 값(v) 쌍의 리스트를 생성한다. 
생성한 리스트의 `키`와 `값`의 위치를 바꿈으로써 새로운 딕셔너리를 생성할 수 있다. 

## 3.18 다차원 리스트 

리스트 항목은 리스트가 될 수 있다. 

list = [ [1, 2, 3], 'Kim', 'doc']

```

위에서 리스트의 리스트에 접근하고 싶다면 list[0][0], list[0][1], list[0][2]를 통해 접근하면 된다.

리스트[행 번호][열 번호]

pandas의 데이터프레임에서의 인덱싱과 헷갈리는 부분인데 명확하게 알아두자