파이썬 리스트 자료구조 이해하기

파이썬 리스트 자료구조 이해하기

파이썬에서 리스트 자료 구조는 또 다른 시퀀스 자료형이다. 문자열과 튜플처럼, 리스트도 0부터 시작하는 정수로 인덱싱 된 항목 들을 담는다.

하지만 튜플과는 달리, 리스트는 가변객체(mutable)이다. 즉, 리스트가 생성되고 난 뒤 인덱스로 접근해 값을 변경할 수 있다는 것이다.


리스트 생성하기

리스트 리터럴(list literal)은 소괄호 대신 대괄호가 사용된다는 점 말고는 튜플과 매우 유사하다.

>>> colors = ["red", "yellow", "green", "blue"]
>>> type(colors)
<class 'list'>

튜플과 마찬가지로, 리스트 리터럴도 동일하지 않은 형태의 값들을 사용할 수 있다. 즉, ["one", 2, 3.0]같은 값도 유효하다.

리스트 리터럴뿐만 아니라, list() 내장함수를 사용해 다른 시퀀스 자료형을 리스트로 생성할 수 있다. 다음 예제는 튜플을 리스트 형태로 생성한다.

>>> list((1, 2, 3))
[1, 2, 3

문자열 또한 리스트로 생성될 수 있다.

>>> list("Python")
['P', 'y', 't', 'h', 'o', 'n']

문자열을 리스트로 생성하는 더 유용한 방법이 존재하는데, 바로 split() 메서드를 사용해 문자열을 리스트 형식으로 만들어주는 방법이다.

>>> groceries = "eggs, milk, cheese"
>>> grocery_list = groceries.split(", ")
>>> grocery_list
['eggs', 'milk', 'cheese']

split() 함수에 인수로 전달된 문자열을 구분자(separator)라고 부르는데, 구분자를 변경함으로써 여러 방법으로 문자열을 리스트로 생성할 수 있다.

>>> # Split string on semi-colons
>>> "a;b;c".split(";")
['a', 'b', 'c']

>>> # Split string on spaces
>>> "The quick brown fox".split(" ")
['The', 'quick', 'brown', 'fox']

>>> # Split string on multiple characters
>>> "abbaabba".split("ba")
['ab', 'ab', '']
정리하자면, 지금까지 다룬 리스트를 생성하는 방법은 다음과 같다.
  1. 리스트 리터럴
  2. list() 내장 함수
  3. 문자열 split() 메서드

기본 리스트 연산

리스트의 인덱싱&슬라이싱 연산도 튜플에서와 동일하게 작동한다.

# 인덱싱
>>> numbers = [1, 2, 3, 4]
>>> numbers[1]
2

# 슬라이싱
>>> numbers[1:3]
[2, 3]

in 연산자를 사용해서 리스트의 요소가 존재하는지를 체크할 수 있다,

>>> "Bob" in numbers
False

리스트는 반복 가능하기(iterable) 때문에 for 반복문에서 반복할 수 있다.

>>> numbers = [1, 2, 3, 4]
>>> for number in numbers:
... 	if number % 2 == 0:
... 		print(number)
...
2
4

리스트의 요소 변경하기

리스트 안의 값을 다른 값으로 바꿀 수 있는 기능을 가변성(mutability)이라고 한다. 앞서 언급했던 것처럼 리스트는 가변객체(mutable)이다. 리스트 안의 값을 다른 값으로 변경하기 위해서는 인덱스에 새로운 값을 할당하면 된다.

>>> colors = ["red", "yellow", "green", "blue"]
>>> colors[0] = "burgundy"
>>> colors
['burgundy', 'yellow', 'green', 'blue']
리스트는 튜플과는 달리 리스트 안의 요소가 변경 가능하다는 점을 기억하자.

리스트 안의 여러 개의 값을 변경하기 위해서는 슬라이스 할당을 사용할 수 있다.

>>> colors = ["burgundy", "yellow", "green", "blue"]
>>> colors[1:3] = ["orange", "magenta"]
>>> colors
['burgundy', 'orange', 'magenta', 'blue']

슬라이스에 할당된 리스트의 길이는 슬라이스과 동일하지 않아도 된다. 예를 들어, 세 가지 요소를 가진 리스트를 두 요소를 가진 슬라이스에 할당할 수 있다.

>>> colors = ["red", "yellow", "green", "blue"]
>>> colors[1:3] = ["orange", "magenta", "aqua"]
>>> colors
['red', 'orange', 'magenta', 'aqua', 'blue']

위 예제를 보면, 값 "orange"와 "magenta"가 변수 colors의 원본 값인 "yellow"와 "green"을 대체했다. 그리고 새로운 4번 인덱스가 생성되고, 인덱스에는 "blue"가 할당되었다. 마지막으로 "aqua"가 3번 인덱스에 할당된다.

# 할당 전 [1:3] 슬라이싱
>>> colors = ["red", "yellow", "green", "blue"]
>>> colors[1:3]
['yellow', 'green']

# 할당
>>> colors[1:3] = ["orange", "magenta", "aqua"]

# 할당 후 
>>> colors[1:3]
['orange', 'magenta']
>>> colors[3]
'aqua'
>>> colors[4]
'blue'

만약 슬라이스에 할당된 리스트의 길이가 슬라이스의 길이보다 적으면 원본 리스트의 전체 길이가 감소한다.

>>> colors
['red', 'orange', 'magenta', 'aqua', 'blue']
>>> colors[1:4] = ["yellow", "green"]
>>> colors
['red', 'yellow', 'green', 'blue']

값 "yellow"와 "green"이 리스트 colors의 "orange"와 "magenta"를 대체하였다. 그리고 인덱스 3번의 "aqua"가 4번 인덱스 "blue"로 대체되었다. 마지막으로 인덱스 4번의 자리는 리스트 colors로 부터 완전히 삭제된다.


리스트 메서드를 사용하여 요소 추가 혹은 삭제하기

슬라이싱으로 리스트의 요소를 추가 혹은 삭제를 할 수 있지만, 파이썬에서는 리스트를 변경하기 위해 더 자연스럽고 가독성이 좋은 리스트 메서드(list method)를 제공한다.

insert()

insert() 메서드는 리스트에 단일 값을 추가하기 위해 사용된다. insert() 메서드는 인덱스와 값, 총 두 개의 매개변수를 취한다.

>>> colors = ["red", "yellow", "green", "blue"]
>>> # "orange"를 두 번째 자리에 삽입
>>> colors.insert(1, "orange")
>>> colors
['red', 'orange', 'yellow', 'green', 'blue']

값 "orange"가 insert() 메서드에서 지정한 1번 인덱스에 삽입이 되고 "yellow"부터 시작하는 기존의 값 들은 오른쪽으로 이동하게 된다.

만약 insert() 메서드의 인덱스 매개변수가 리스트의 최대 인덱스보다 크다면, 그 값은 인덱스의 가장 끝에 위치하게 된다.

>>> colors.insert(10, "violet")
>>> colors
['red', 'orange', 'yellow', 'green', 'blue', 'violet']

# violet의 인덱스
>>> colors[4]
'violet'

insert() 메서드는 음수 인덱스와도 사용할 수 있다. 다음의 예제에서 값 "indigo"는 인덱스 -1에 삽입되고, 기존의 인덱스 -1인 "violet"은 오른쪽으로 한칸 이동한다.

>>> colors.insert(-1, "indigo")
>>> colors
['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet']

pop()

pop() 메서드는 하나의 매개변수(인덱스)를 취하고, 받은 인덱스에 위치한 값을 제거한다. 제거된 값은 메서드에 의해 반환된다.

>>> colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet']
>>> color = colors.pop(3)
>>> color
'green'
>>> colors
['red', 'orange', 'yellow', 'blue', 'indigo', 'violet']

insert() 메서드와는 달리, pop() 메서드에 리스트의 최대 인덱스보다 큰 인수를 사용하면 파이썬에서 IndexError를 발생한다.

>>> colors.pop(10)
Traceback (most recent call last):
  File "<pyshell#0>", line 1, in <module>
	colors.pop(10)
IndexError: pop index out of range

pop() 메서드는 음수 인덱스와도 사용될 수 있다.

>>> colors = ['red', 'orange', 'yellow', 'blue', 'indigo', 'violet']
>>> colors.pop(-1)
'violet'
>>> colors
['red', 'orange', 'yellow', 'blue', 'indigo']

append()

append() 메서드는 리스트 끝에 새로운 요소를 추가하기 위해 사용된다.

>>> colors = ['red', 'orange', 'yellow', 'blue']
>>> colors.append("indigo")
>>> colors
['red', 'orange', 'yellow', 'blue', 'indigo']

append() 메서드는 insert() 메서드를 사용해서 리스트보다 인덱스가 크거나 혹은 동등한 길이에 요소를 추가하는 것과 동일하다.

>>> colors.insert(len(colors), "indigo")

append() 메서드는 insert() 메서드를 사용하는 것 보다 짧고 서술적이다. 또한, append() 메서드로 리스트 끝에 요소를 추가하는 방법이 일반적으로 더 파이써닉 하다고 여겨진다.

extend()

extend() 메서드는 리스트 끝에 '여러' 새로운 요소를 추가하기 위해 사용된다.

>>> colors = ['red', 'orange', 'yellow', 'blue', 'indigo']
>>> colors.extend(["violet", "ultraviolet"])
>>> colors
['red', 'orange', 'yellow', 'blue', 'indigo', 'violet', 'ultraviolet']

extend() 메서드는 반복 가능한 유형(iterable type)의 단일 매개변수를 취한다. 매개변수로 전달된 이터러블의 요소들은 전달된 순서와 동일하게 추가된다.

일반적으로 extend() 메서드에 리스트를 전달하지만 튜플도 사용이 가능하다.

>>> colors.extend(("violet", "ultraviolet"))

위 네 가지 리스트 메서드를 테이블로 정리하면 다음과 같다.


리스트와 숫자 연산

리스트를 숫자와 사용하는 가장 흔한 작업 중 하나가 모든 값을 합해서 합계를 구하는 것이다.

>>> nums = [1, 2, 3, 4, 5]
>>> total = 0
>>> for number in nums:
... total = total + number
...
>>> total
15

for 반복문을 사용하는 것도 매우 직관적이지만, 파이썬에서 제공하는 내장 함수인 sum() 함수를 사용하는 것이 더 간결하다. sum() 함수는 리스트를 인수로 취하고 리스트 안에 모든 값의 총 합을 반환한다.

>>> sum([1, 2, 3, 4, 5])
15

만약 sum() 함수에 전달된 리스트가 숫자형이 아닌 값을 포함하면 TypeError가 발생한다.

>>> sum([1, 2, 3, "four", 5])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'

파이썬에서는 최소 값을 반환하는 min() 내장 함수와 최대 값을 반환하는 max() 내장 함수 또한 제공한다.

>>> min([1, 2, 3, 4, 5 ])
1
>>> max([1, 2, 3, 4, 5])
5

sum(), min(), 그리고 max() 함수는 튜플과도 사용할 수 있다.

>>> sum((1, 2, 3, 4, 5))
15
>>> min((1, 2, 3, 4, 5))
1
>>> max((1, 2, 3, 4, 5))
5

리스트 컴프리헨션

이미 존재하는 이터러블로부터 리스트를 생성하는 또 다른 방법은 리스트 컴프리헨션(list comprehension)을 사용하는 것이다. 리스트 컴프리헨션을 사용하면 리스트 안에 for 반복문이나 if 조건문 같은 식을 사용하여 리스트를 생성할 수 있다.

>>> numbers = (1, 2, 3, 4, 5)
>>> squares = [num**2 for num in numbers]
>>> squares
[1, 4, 9, 16, 25]

위 예제처럼 리스트 컴프리헨션을 사용하면 짧은 코드로 리스트를 생성할 수 있다. 예제 첫 번째 줄에서는, 숫자 다섯 개를 담고 있는 튜플 리터럴이 생성되었고 변수 numbers할당 되었다. 두 번째 줄에는 리스트 컴프리헨션을 사용해서 각 숫자(num)에 접근해 각 숫자를 제곱하고, 새로운 리스트 squares에담아준다.

리스트 컴프리헨션을 사용한 위 예제를 기존 for 반복문으로 만들면 다음과 같다.

>>> squares = []
>>> for num in numbers:
... 	sqaures.append(num**2)
...
>>> squares
[1, 4, 9, 16, 25]

리스트 컴프리헨션을 사용해 기존 값을 다른 타입으로 변경할 수 있다. 예를 들어, 다음 예제는 부동 소수점을 포함하는 문자열의 리스트를 float 객체로 변환한다.

>>> str_numbers = ["1.5", "2.3", "5.25"]
>>> float_numbers = [float(value) for value in str_numbers]
>>> float_numbers
[1.5, 2.3, 5.25]

리스트 컴프리헨션은 파이썬에서만 사용되는 방법은 아니지만, 파이썬에서 자주 사용되는 방식 중 하나이다. 만약 빈 리스트를 생성하거나, 다른 이터러블로 반복할 때, 리스트에 새로운 아이템을 추가할 때 등 기존의 코드를 리스트 컴프리헨션으로 대체할 수 있다.

Reference