• Home
  • About
    • Yerim Oh photo

      Yerim Oh

      Happy and worthwhile day by day :)

    • Learn More
    • Email
    • LinkedIn
    • Instagram
    • Github
    • Youtube
  • Posts
    • All Posts
    • All Tags
  • Projects

[07] Concept(리스트)

07 Mar 2020

Reading time ~5 minutes

Table of Contents
  • 리스트 목차
  • 리스트 List
  • 특성
  • 실행 가능한 연산
  • 리스트의 활용 방법
    • 리스트 선언 방법
    • 삽입
      • append()
      • insert()
      • 문자와 불리언 삽입
    • 추출
      • 슬라이싱 slicing, 예외처리
    • 요소 삭제 2가지 방법
      • 인덱스로 삭제
      • 값으로 삭제
  • 리스트의 특징
    • 장점
    • 단점

리스트 목차

  • 리스트 List
  • 특성
  • 실행 가능한 연산
  • 리스트의 활용 방법
    • 리스트 선언 방법
    • 삽입
      • append()
      • insert()
      • 문자와 불리언 삽입
    • 추출
      • 슬라이싱 slicing, 예외처리
    • 요소 삭제 2가지 방법
      • 인덱스로 삭제
      • 값으로 삭제
  • 리스트의 특징
    • 장점
    • 단점

리스트 List

특성

순서대로 저장하는 시퀀스이자 변경 가능(Mutable List)한 목록
입력 순서가 유지
내부적으로는 동적 배열로 구현됨

  • 파이썬뿐만 아니라 현대 언어들은 언어별로 대부분 동적 배열을 지원
  • C++나 자바: 동적 배열은 가장 자주 쓰는 자료형 중 하나.
  • 파이썬에서도 가장 자주 쓰는 자료형으로서, 특히 코딩 테스트 시 매우 빈번하게 활용됨.

[각 언어 별 동적 배열 구현]

언어 동적배열
파이썬 list
C++ std::vector
자바 ArrayList

[장점] 리스트를 사용하면 스택을 사용할지, 큐를 사용할지(9장)를 고민 필요 없음
(이유)스택과 큐에서 사용 가능한 모든 연산을 함께 제공.


실행 가능한 연산

연산 시간 설명
len(a) O(1) 전체 요소의 개수 리턴
a[i] O(1) 인덱스 i의 요소를 가져옴
a[i:j] O(k) 1) i부터 j까지 슬라이스의 길이만큼인 k개의 요소를 가져옴
    2) 객체 k개에 대한 조회가 필요하므로 O(k)
elem in a O(n) 1) elem 요소가 존재하는지 확인
    2) 처음부터 순차 탐색하므로 n만큼 시간이 소요
a.count(elem) O(n) elem 요소의 개수를 리턴
a.index(elem) O(n) elem 요소의 인덱스를 리턴
a.append(elem) O(1) 리스트 마지막에 elem 요소를 추가
a.pop() O(1) 1) 리스트 마지막 요소를 추출
    2) 스택의 연산
a.pop(0) O(n) 1) 리스트 첫번째 요소를 추출
    2) 큐의 연산
    3) 전체 복사가 필요하므로 O(n)
    4) 큐의 연산을 이용한다면 리스트보다는 O(1)에 가능한 데크(deque)를 권장(나중에 설명)
del a[i] O(n) 1) i에 따라 다름
    2) 최악의 경우 O(n)
a.sort() O(n log n) 1) 정렬
    2) 팀소트(Timsort)를 사용 (6장 링크)
    3) 최선의 경우 O(n)에도 실행될수 있음
min(a), max(a) O(n) 최솟값/최댓값을 계산하기 위해서는 전체를 선형 탐색
a.reverse() O(n) 1) 뒤집음
    2) 리스트는 입력 순서가 유지되므로 뒤집게 되면 입력 순서가 반대로 됨
  • 리스트의 경우 탐색 시 값의 존재 유무를 확인하려면, 정렬된 경우에는 이진 검색이 효율적(18장)

리스트의 활용 방법

자세히 알아보기

리스트 선언 방법

>>> a = list()

대괄호( [] )로 좀 더 간단하게 선언 가능

>>> a = []

초깃값을 지정해 선언

>>> a = [1, 2, 3] 
>>> a
[1, 2, 3]

삽입

append()

>>> a = [1, 2, 3] 
>>> a
[1, 2, 3]

>>> a.append(4) 
>>> a
[1, 2, 3, 4]

insert()

: 특정 위치의 인덱스를 지정해 요소를 추가 가능.

# 3번째 인덱스에(인덱스는 0부터 시작한다) 5를 삽입한다.
>>> a.insert(3, 5) 
>>> a
[1, 2, 3, 5, 4]

문자와 불리언 삽입

리스트는 숫자 외에도 다양한 자료형을 단일 리스트에 관리 가능
-> 매우 편리.

다른 언어

  • 동적 배열에 삽입할 수 있는 자료형을 __동일한 타입으로 제한__하는 경우가 많은 데 비해, 파이썬은 이처럼 자유롭게 삽입
>>> a.append('안녕') 
>>> a.append(True) 
>>> a
[1, 2, 3, 5, 4, '안녕', True]

추출

인덱스 지정하면 추출 가능

>>> a[3]
5

슬라이싱 slicing, 예외처리

특정 범위 내의 값을 매우 편리하게 가져올 수 있음.

원래 슬라이싱은 문자열에 유용하게 활용되는 기능으로서, 리스트에도 동일한 형태로 매우 유용하게 활용 가능.

다른 언어들

  • 인덱스의 반복문을 구성하고 순회하면서 값을 출력해야 함,

파이썬의 슬라이싱은 이처럼 시작 인덱스와 종료 인덱스를 설정해 간단히 해당하는 값 출력 가능
리스트이름[첫번째 인덱스: 끝 인덱스: step(생략가능 default = 1)]

>>> a[1:3]
[2, 3]

시작 인덱스는 다음과 같이 생략 가능
: 이 경우 처음부터 값을 가져옴

>>> a[:3]
[1, 2, 3]
종료 인덱스를 생략 가능
마지막까지 값을 가져온다
>>> a[4:]
[4, '안녕', True]

홀수 번째 인덱스의 값만 가져올 수도 있음

# 인덱스 1, 2, 3의 값 
>>> a[1:4]
[2, 3, 5]

# 인덱스 1, 3의 값 
>>> a[1:4:2]
[2, 5]
존재하지 않는 인덱스를 조회할 경우
다음과 같이 IndexError 가 발생 ```python

a[9] Traceback (most recent call last): File “", line 1, in IndexError: list index out of range

IndexError 는 인덱스가 리스트의 길이를 넘어설 때 발생하며


**try 구문으로 에러에 대한 예외 처리 가능.**

```python
try:
print(a[9]) 
except IndexError:
print('존재하지 않는 인덱스')

요소 삭제 2가지 방법

1) 인덱스로 삭제하기 2) 값으로 삭제하기

인덱스로 삭제

[del 키워드]
인덱스의 위치에 있는 요소를 삭제할 수 있다.

>>> a
[1, 2, 3, 5, 4, '안녕', True]
>>> del a[1] 
>>> a
[1, 3, 5, 4, '안녕', True]

값으로 삭제

[remove() 함수]
값에 해당하는 요소를 삭제할 수 있다.

>>> a
[1, 3, 5, 4, '안녕', True]
>>> a.remove(3) 
>>> a
[1, 5, 4, '안녕', True]

[pop() 함수]
스택의 팝 pop 연산처럼 추출로 처리.
즉 삭제될 값을 리턴 후 삭제

>>> a
[1, 5, 4, '안녕', True]
>>> a.pop(3)
'안녕'
>>> a
[1, 5, 4, True]


리스트의 특징

장점

연속된 공간에 요소를 배치하는 배열의 장점
+ 다양한 타입을 연결해 배치하는 연결 리스트의 장점

= 배열과 연결 리스트가 모두 필요 없을 정도로 강력 -> 때문에 파이썬은 아예 원시 타입 자료형은 제공하지도 않음.

다음은 CPython에서 리스트를 정의한 헤더 파일의 일부.

// cpython/Include/cpython/listobject.h 
typedef struct { 
PyObject_VAR_HEAD 
PyObject **ob_item;
Py_ssize_t allocated;
} PyListObject;

CPython에서 리스트는 요소에 대한 포인터 목록( ob_item )을 갖고 있는 구조체 Struct 로 선언되어 있음.
리스트에 요소를 추가하거나 조작하기 시작하면 ob_item 의 사이즈를 조절해 나가는 형태로 구현되어 있음.

리스트는 이처럼 객체로 되어 있는 모든 자료형을 다음과 같이 포인터로 연결

파이썬은 모든 것이 객체 파이썬의 리스트

  • 그림 5-2처럼 이들 객체에 대한 포인터 목록을 관리하는 형태로 구현.
  • 사실상 연결 리스트에 대한 포인터 목록을 배열 형태로 관리,
    -> 그 덕분에 파이썬의 리스트는 배열과 연결 리스트를 합친 듯이 강력한 기능을 자랑.
    image

다음은 정수로 이뤄진 값들을 파이썬 리스트에 삽입하는 코드다.

>>> a = [1, 2, 3] 
>>> a
[1, 2, 3]

일반적으로 정수형 배열이라고 하면, 이처럼 정수로만 이뤄진 값을 연속된 메모리 공간에 저장하는 경우를 말하며, 정수가 아닌 값은 저장할 수 없다. 그러나 파이썬의 리스트는 연결 리스트에 대한 포인터 목록을 관리하고 있기 때문에 앞서 ‘리스트의 활용 방법’(링크삽입) 절에서 살펴본 것처럼 다음과 같이 정수, 문자, 불리언 등 제각기 다양한 타입을 동시에 단일 리스트에서 관리하는 게 가능.

>>> a = [1, '안녕', True] 
>>> a
[1, '안녕', True]

단점

  • 각 자료형의 크기는 저마다 서로 다름
    -> 이들을 연속된 메모리 공간에 할당 불가능
  • 결국 각각의 객체에 대한 참조로 구현할 수밖에 없음.
  • 인덱스를 조회 시
    모든 포인터의 위치를 찾아가서 타입 코드를 확인하고 값을 일일이 살펴봐야 하는 등 추가적인 작업이 필요
    -> 속도 면에서도 훨씬 더 불리


Coding test Share Tweet +1