Table of Contents
- 리스트
리스트
파이썬 언어는 개발자의 편의성, 생산성, 가독성에 가장 초점을 맞춘 언어입니다. 그래서 파이썬에서는 기존 언어에서 동적 배열이라는 기초 자료형의 불편함을 개선해 리스트라는 파이썬만의 강력한 자료형을 제공합니다.
1. 리스트 자료형의 특징
- 파이썬에서 가장 자주 사용하는 자료형
- 원소들의 순서가 있는 시퀀스
- 원소들의 변경이 가능 (Mutable)
- 다양한 타입의 원소 저장 가능
- 동적배열로 구현됨
2. 리스트의 장점
- 임의의 원소에 O(1) 접근 가능: 이것은 기존 동적배열이 제공해주는 기능입니다
- 다양한 타입의 원소 저장 가능: 리스트가 값이 아닌 값을 가진 객체의 주소를 동적배열로 저장하고 있기 때문입니다.
- 왠만한 추상 자료형은 리스트로 구현 가능: 리스트 자료형이 가지고 있는 많은 메소드로 스택, 큐, 트리, 그래프 등 거의 모든 추상 자료형을 구현할 수 있습니다.
3. 리스트 생성
리스트는 여러 가지 자료형을 가질 수 있는 시퀀스형 자료형입니다.
또한 값을 변경할 수 있습니다.
>>> a = [1, 2, 3, 4]
>>> b = [3.1, 2.5, 7]
>>> c = ["Hello", "Good"]
>>> d = [1, 4.5, "Good"]
>>> a[0] = 100
>>>a
[100, 2, 3, 4]
🔔 리스트를 곱하거나 더하면 값이 반복되거나 추가됩니다
>>> a = [1, 2, 3, 4]
>>> a + [5]
[1, 2, 3, 4, 5]
>>> a + 5 -> Error
>>> a * 2
[1, 2, 3, 4, 1, 2, 3, 4]
4. 인덱싱, 슬라이싱
이번에는 위에서 만들어진 리스트 데이터를 가지고 원하는 부분만 가져올 수 있도록 해주는 인덱싱, 슬라이싱에 대해 알아보겠습니다.
>>> a = [1, 2, 3, 4]
"""인덱싱"""
>>> a[0]
1
>>> a[2]
3
>>> a[4] = 10 -> Error
(a[50] = 10 이런식으로 하면 그 사이의 인덱스에 값을 표시할 수 없어서 무조건 차례대로 값을 채워넣어야 함 -> 더하기 또는 append 메소드)
"""슬라이싱"""
>>> a[0:3] # 0에서 부터 3앞까지 -> 인덱스 0~2
[1, 2, 3]
>>> a[:]
[1, 2, 3, 4]
>>> a[::]
[1, 2, 3, 4]
>>> a[::-1] #처음부터 끝까지 거꾸로 슬라이싱 (중요)
[4, 3, 2, 1]
5. 리스트 메소드
리스트 데이터는 프로그래밍을 하다보면 정말 자주 만나게 되는 자료형 중에 하나입니다.
그렇기 때문에 문자열 객체의 메소드를 잘 활용할 줄 아는 것이 굉장히 중요합니다.
먼저 어떤 메소드가 있는지 확인해 보겠습니다.
>>> a = [1, 2, 3, 4]
>>> dir(a)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__',
'__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__',
'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
실제로 코딩을 하실 때는 기억이 안나면 그 때마다 dir() 함수를 사용해 어떤게 있는지 살펴보면 됩니다.
5-1 .append(), .extend(), .insert(), .copy()
.append()
리스트 맨 끝에 인자로 넣어준 값 하나를 추가해준다.
>>> a = [1, 2, 3, 4]
>>> a.append(100)
>>> a
[1, 2, 3, 4, 100]
여러 개를 추가하고 싶어서 인자로 값을 여러 개 준다면? -> 에러가 난다
그래서 [100, 101, 102] 이런 식으로 추가하면? -> 에러는 안나지만 리스트가 추가되어 원하는 모습과는 다르다.
.extend()
iterable한 객체를 인자로 넣어주면 그 안의 원소들이 모두 차례대로 리스트에 추가된다.
>>> a.extend([101, 102, 103]) # 리스트와 같은 iterable한 객체를 인자로 주어야 한다.
>>> a
[1, 2, 3, 4, 100, 101, 102, 103]
이제는 맨 뒤가 아니라 원하는 인덱스에 값을 추가(교체)하고 싶다.
.insert()
인자로 인덱스와 값을 넣어주면 인덱스에 값을 넣어준다.
인덱스에 이미 값이 있으면 바꿔주고 리스트 길이보다 인덱스 값이 크거나 같으면 리스트 맨 뒤에 값을 넣어준다.
-> 길이 신경쓰지 않고 해줘도 오류는 안난다. (내가 원하는 인덱스에 값이 들어가지 않을 수도 있지만)
>>> a = [1, 2, 3, 4]
>>> a.insert(1, 10)
>>> a
[1, 10, 3, 4]
>>> a.insert(1000, 7)
>>> a
[1, 10, 3, 4, 7]
.copy()
객체와 똑같은 값을 가지는 리스트를 복사한다. 변수를 지정해주면 새로운 메모리에 저장된다.
>>> a = [1, 2, 3, 4]
>>> b = a.copy()
>>> b.append(5)
>>> a
[1, 2, 3, 4]
>>> b
[1, 2, 3, 4, 5]
5-2 .pop(), .remove(), .clear()
.pop()
리스트의 가장 끝에 있는 원소를 뽑아 리턴해준다.
>>> a = ['banana', 'lemon', 'apple']
>>> a.pop()
'apple'
>>> a
['banana', 'lemon']
.remove()
인자로 받은 값은 값을 제거해준다.
>>> a.remove(2)
>>> a
[1, 3, 4]
.clear()
리스트를 싹 비운다.
>>> a.clear()
>>> a
[]
5-3 .sort(), .reverse()
.sort()
리스트를 작은 값부터 순서대로 정렬해준다.
>>> a = [3, 5, 1, 2, 4]
>>> a.sort()
>>> a
[1, 2, 3, 4, 5]
>>> a = ['안녕', 'Hello', 'Hi', '안녕하십니까']
>>> a.sort(key=len)
>>> a
['안녕', 'Hi', 'Hello', '안녕하십니까']
>>> a = [3, -9, -1, 1, 2, 11]
>>> a.sort(key=lambda x: x**2)
>>> a
[-1, 1, 2, 3, -9, 11]
# 같은 제곱값에 대해 양수가 먼저 나오게 하려면 양수가 논리연산 시 False가 되면 되므로 기준을 0보다 작은지로 하면 된다
>>> a = [3, -9, -1, 1, 2, 11]
>>> a.sort(key=lambda x: (x**2, x<=0))
>>> a
[-1, 1, 2, 3, -9, 11]
>>> a =[False, True, False, True, True, False]
>>> a.sort()
>>> a
[False, False, False, True, True, True]
🔔 sorted() 함수
- sorted() 함수는 정렬된 값을 리턴해줄 뿐 인자로 받은 리스트를 정렬하지는 않는다.
- 또 한가지 중요한 특징은 sorted()함수는 리스트 뿐 아니라 모든 iterable한 값들을 정렬시켜 준다는 것입니다.
>>> a = [3, 5, 1, 2, 4]
>>> sorted(a)
[1, 2, 3, 4, 5]
>>> a
[3, 5, 1, 2, 4]
>>> sorted({1: 'D', 2: 'B', 3: 'B', 4: 'E', 5: 'A'})
[1, 2, 3, 4, 5]
🔔 .sort()와 sorted() 모두 key, reverse 인자를 갖는다
- key: 정렬을 목적으로 하는 함수를 값으로 넣는다. lambda를 이용할 수 있다. key 매개 변수의 값은 단일 인자를 취하고 정렬 목적으로 사용할 키를 반환하는 함수(또는 다른 콜러블)여야 합니다.
- reverse: bool값을 넣는다. 기본값은 reverse=False(오름차순)이다.
.reverse()
리스트의 원소의 순서를 뒤집어준다.
>>> a = [1, 2, 3, 4]
>>> a.reverse()
>>> a
[4, 3, 2, 1]
🔔 reversed() 함수
뒤집은 값을 리턴해줄 뿐 인자로 받은 리스트는 그대로다.
🔔 문자열을 뒤집는 방법
a.reverse()
a = list(reversed(a))
a = a[::-1]
5-4 .count(), .index()
.count()
인자로 받은 값이 등장하는 횟수를 리턴해준다.
a = [1, 1, 1, 2, 3, 4]
a.count(1)
---------------------
3
.index()
인자로 받은 값의 인덱스를 리턴해준다.
a = [1, 3, 5, 7]
a.index(7)
--------------------
3
6. 리스트에서 주목할 만한 것들
6-1 List & Range
1부터 1000까지 값을 하나씩 출력하는 코드를 짠다고 할 때
for i in [1, 2, 3, 4, 5, 6, 7, 8, ..., 1000]:
print(i)
로 하게 되면 위의 코드를 실행하기 위해 1000개의 요소를 적어서 리스트를 만드는 것은 너무 비효율적입니다.
이를 개선시키는 방법으로
for i in range(1000):
print(i)
이렇게 해주면 훨씬 짧고 간결한 코드를 작성할 수 있습니다.
range(start, end, step)
range(1000) => 0, 1, 2, 3, ..., 999
range(1, 1000) => 1, 2, 3, ..., 999
range(1, 1000, 2) => 1, 3, 5, 7, ..., 999
6-2 리스트 표현식 (List comprehension)
>>> a = []
>>> for i in range(100):
if i % 3 == 0 and i % 5 == 0:
a.append(i)
>>> [i for i in range(100) if i % 3 == 0 and i % 5 == 0]
6-3 리스트와 문자열 넘나들기
문자열을 리스트로 바꿔야 하는 경우
문자열은 값을 바꿀 수가 없기 때문에 예를 들어 스펠링을 고치기 위해서는
리스트로 바꿔서 고친 후 다시 문자열로 변환해줘야 한다.
>>> name = 'kinziont'
>>> name[2] = 'm' -> 에러
>>> name = list(name)
>>> name[2] = 'm'
>>> name
['k', 'i', 'm', 'z', 'i', 'o', 'n', 't']
>>> name = str(name)
>>> name
'kimziont'
문자열 데이터를 단어 단위 또는 문장 단위로 토크나이징하기 위해 문자열 메소드인 .split()을 쓰면
자동으로 리스트로 변환된다.
6-4 리스트를 이용한 다차원 데이터 표현하기 (Tensor)
a = [1, 2, 3, 4] # 1*4 vector
b = [[1, 2], [3, 4]] # 2*2 matrix
c = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]] # 2*2*2 tensor
a[0] -> 1
b[0] -> [1, 2]
c[0] -> [[1, 2], [3, 4]]
c[0][1] -> [3, 4]
c[0][1][0] -> 3
7. 리스트 주요 연산들의 시간 복잡도
연산 | 시간 복잡도 | 설명 |
len(a) | O(1) | 전체 요소의 개수를 리턴 |
a[i] | O(1) | 인덱스 i의 요소를 가져온다 |
a[i:j] | O(k) | 객체 k개에 대한 조회가 필요하므로 O(k)이다 |
x in a | O(n) | 정렬되어 있지 않은 a 이므로 순차 탐색 |
a.append(x) | O(1) | 동적배열의 특징 |
a.pop(x) | O(1) | 동적배열의 특징 |
a.pop(0) | O(n) | 배열의 특성상 앞의 원소가 추가/삭제 되면 그 뒤의 모든 원소들의 이동이 발생 |
del a[i] | O(n) | i에 따라 다르다. 최악의 경우 O(n)이다 |
a.sort() | O(nlogn) | 파이썬에서는 팀소트(Timsort)를 사용 |
min(a), max(a) | O(n) | 최소, 최대값 찾기 위해서는 선형 탐색 해야함 |
a.reverse() | O(n) | 선형 이동하면서 처음과 끝 원소 바꾼다 |