개요
리스트는 프로그래머들의 경험이 잘 녹아있는 자료형이다. 오래된 프로그래밍 언어인 C++ 에서는 vector<> 라는 자료형과 같으며, 그외에는 동일한 list 라는 자료형을 사용하고 있다.
리스트형은 내부에 같은 리스트를 포함한 자료형들 즉 데이터를 담을 수 있다. 이렇게 담는 의미가 강한 자료형을 container 자료형으로 불리기도 한다. 그리고 리스트내에 담긴 데이터를 원소라고 부른다.
또한 리스트는 순서가 있는 자료형이다. 이러한 순서(혹은 순번)을 index라고 표현하며, 첫 원소의 index는 0이다.
list 의 index가 0부터 시작하는 이유
현재 대부분의 프로그래밍 언어의 문법들은 C 에서 많은 영향을 받았다. 이 때 C 에서는 list와 같은 연속적인 데이터를 저장하기 위해서 array 자료형을 제공하였다. 이 array는 연속적인 데이터가 시작되는 메모리의 위치를 의미했다.
그리고 array의 작동원리는 메모리의 특정 시작지점으로 부터 얼만큼(방향은 부호) 떨어진 곳을 읽느냐는 의미로 설계 되어있다보니 자연스럽게 메모리에서 시작지점에 저장된 데이터를 읽으려면 0 부터 시작하게 되었다.
그리고 기존의 많은 프로그래머들은 이 방식이 익숙해졌다. 결국 대부분의 연속형 자료형의 인덱스는 0 부터 시작하도록 만들어지고 사용되고 있다.
정의
리스트는 대괄호로 정의할 수 있다. 리스트에 원소(혹은 요소)로 담기는 자료형은 제약이 없다. 심지어 리스트도 담길 수 있다.(그러나 좋은 코드는 규칙성이 있게 담거나 특정 자료형을 모아서 담는 것이 좋다.)
a = []
a
# []
b = [1, 2, 3]
b
# [1, 2, 3]
c = ["1", "hello", 'world']
c
# ["1", "hello", "world"]
d = [1, 'hello', b]
d
# [1, "hello", [1, 2, 3]]
연산
리스트간 연산이 가능하다. 다만 자세히 관찰하면 문자열의 연산과 유사점이 있다.
list + list
두 개의 리스트 덧셈을 할 경우 연산자의 좌측 리스트에 이어서 연사자의 우측 리스트가 연결이 된 리스트를 반환한다. 문자열의 덧셈 연산과 유사하다. 다만, 문자열의 덧셈과 차이점은 문자열의 경우 동일한 문자열 자료형만 덧셈이 가능하다. 반면 리스트는 어떤 원소가 있어도 상관이 없다.
a = [1, 2]
b = [3, 4]
c = a + b
c
# [1, 2, 3, 4]
list += list
할당 연산자와 동일하게 동작한다. 연산자 좌측 리스트와 우측 리스트의 덧셈 연산을 한 결과를 좌측 리스트 변수에 대입하게 된다.
a = [1, 2]
b = [3, 4]
a += b
a
[1, 2, 3, 4]
list * int
앞의 list를 뒤에 있는 정수 만큼 더한 것과 같다. 문자열의 곱연산과 같다.
a = [1, 2]
b = a * 3
b
# [1, 2, 1, 2, 1, 2]
인덱싱[int]
리스트는 순서 즉 index가 있기때문에 처음 원소를 0번으로 정의하고 이후부터는 순서대로 번호를 붙일 수 있다. 이러한 번호로 읽거나 수정이 가능하다. 이런 대괄호 기호를 인덱싱 연산자라 부른다.
주의할 점은 인덱싱 연산자 내에 들어가는 숫자는 반드시 정수가 되야 한다. 그리고 인덱싱 연산자내에 범위를 벗어나는 index가 들어간 경우 에러가 발생한다.
# IDLE Shell
# 0 1 2 3
a = [1, 3, 5, 7]
a[0]
# 1
a[2]
# 5
a[2] = 0
a[2]
# 0
a[4] = 1
# Error: indexError
a[1.1]
# Error
파이썬의 독특한 점은 인덱싱에 음수를 사용할 수 있다. 인덱스에 -1 이 들어갈 경우 리스트의 가장 마지막 원소를 반환한다. 그리고 음수에서 작아질 수록(숫자가 커질 수록) 역순으로 반환하게 된다.
# IDLE Shell
# 0 1 2
a = ["hello", "world", "??"]
a[-1]
# "??"
a[-2]
# "world"
리스트 속에 있는 리스트에 접근할 경우 가장 좌측 인덱스가 가장 바깥부터 접근을 한다.
# IDLE Shell
# 0 1 2
a = ["hello", "world", "??"]
a[-1]
# "??"
a[-2]
# "world"
위의 예시의 경우 리스트 문법의 동작 원리를 설명하기 위한 것으로 실무에서는 이러한 복합적인 구조를 거의 사용하지 않는다. 완전한 2차원 리스트 혹은 완전한 3차원 형태로 사용하는 경우는 있다. 이는 프로그래밍을 할때 최대한 단순한 구조로 하는 것이 유지 보수에 유리하기 때문이다.
슬라이싱
리스트의 특정 구간을 자를 수 있다. 범위를 지정할 때는 기호 콜론(:)을 사용한다. 만약 처음이나 끝을 의미할 경우 해당 인덱스 값을 굳이 안넣어도 된다. 직관적으로 1:3은 마치 1~3 과 같은 의미가 된다.
# IDLE Shell
# 0 1 2 3 4
a = [1, 3, 5, 7, 9]
b = a[1:3]
b
# [3, 4, 7]
c = a[:3]
c
# [1, 3, 5, 7]
d = a[2:]
d
# [5, 7, 9]
대입
리스트의 대입 연산은 일반적인 숫자 대입과 다르다. 일반적인 숫자를 대입할 경우에는 값이 복사되어 변수에 담긴다. 이렇나 자료들은 immutable이라 한다. 하지만 리스트와 같은 자료형은 mutable로 구분되어 대입연산으로 다른 변수에 대입을 해도 값을 복제하지 않고 값을 공유한다.
만약 C를 공부한적이 있다면, mutable은 얕은 복사, immutable은 깊은 복사와 거의 같은 개념이다. immutable과 mutable 자료형에 대해서는 클래스 이후에 다시 살펴 보도록 하자.
# immutable
va = 1
vb = va
va = 3
va
# 3
vb
# 1
# mutable
la = [1, 2, 3]
lb = la
la[1] = 4
lb
# [1, 4, 3]
# 아래의 경우 la에 새로운 리스트가 할당되고
# lb는 기존의 리스트가 유지 된다
la = [4, 5, 6]
lb
# [1, 4, 3]
언박싱(unboxing)
파이썬의 독특한 문법으로 복수의 변수에 리스트를 대입 연산을 시도할 경우 변수명의 개수와 리스트의 원소 개수가 같은 경우 각각의 원소가 순서대로 변수에 들어가게 된다. 이때 리스트 자료형이 풀어지는 것과 같다(그래서 언박싱이라 이름을 지은 듯하다).
a = [1, 2, 3]
b, c, d = a
b
# 1
c
# 2
d
# 3
element in list
리스트 내에 동일한 원소가 있을 경우 True, 없을 경우 False를 반환한다.
a = [1, 2, 3, 4]
1 in a
# True
0 in a
# False
함수
len()
리스트의 길이를 반환해준다. 리스트의 길이는 리스트에 들어 잇는 원소의 개수이다. 리스트와 유사한 문자열에서도 사용된다.
a = [1, 2, 3, 4]
len(a)
# 4
len([1, 2, 3, 4, 5])
# 5
del
리스트에 있는 원소를 삭제해준다. 인덱싱과 슬라이싱을 응용하여 사용이 가능하다. 주의해야 할 점은 index로 삭제할 경우 기존 index가 삭제되면서 index가 바뀌게 되는 점이다. index가 바뀌면서 발생하는 문제를 피하기위해서 역순으로 삭제하는 요령이 있다.
# 0 1 2 3 4
a = [1, 2, 3, 4, 5]
del a[1]
a
# [1, 3, 4, 5]
del a[2:]
a
# [1, 3]