list는 _순서_가 있는 항목들의 변경 가능한 컬렉션이에요.
대부분의 컬렉션과 마찬가지로(내장된 tuple, dict, set 참고) 배열은 다른 배열을 포함해 어떤 (또는 여러) 데이터 타입도 참조할 수 있어요.
다른 시퀀스과 마찬가지로, 항목은 왼쪽에서 0-based index 번호로, 오른쪽에서 -1-based index로 접근할 수 있어요.
배열은 슬라이스 표기법이나 <list>.copy()로 전체 또는 일부를 복사할 수 있어요.
배열은 min()/max(), <list>.index(), <list>.append(), <list>.reverse() 같은 공통 시퀀스 연산과 변경 가능 시퀀스 연산을 모두 지원해요.
배열의 원소는 for item in <list> 구문으로 순회할 수 있어요.
for index, item in enumerate(<list>)는 원소의 인덱스와 값이 모두 필요할 때 사용할 수 있어요.
내부적으로 lists는 Java의 ArrayList 타입과 비슷한 동적 배열로 구현되며, 길이를 알 수 없는 비슷한 데이터(문자열, 숫자, 집합 등)의 묶음을 저장할 때 가장 많이 사용해요.
배열은 매우 유연하고 유용한 자료 구조이며, Python의 많은 내장 메서드와 연산은 결과로 배열을 만들어 내요.
list는 대괄호 []와 원소 사이의 쉼표를 사용해 _리터럴_로 선언할 수 있어요:
>>> no_elements = []
>>> no_elements
[]
>>> one_element = ["Guava"]
>>> one_element
['Guava']
>>> elements_separated_with_commas = ["Parrot", "Bird", 334782]
>>> elements_separated_with_commas
['Parrot', 'Bird', 334782]
가독성을 위해, list 안에 원소나 중첩된 자료 구조가 많을 때는 줄 바꿈을 사용할 수 있어요:
>>> lots_of_entries = [
"Rose",
"Sunflower",
"Poppy",
"Pansy",
"Tulip",
"Fuchsia",
"Cyclamen",
"Lavender"
]
>>> lots_of_entries
['Rose', 'Sunflower', 'Poppy', 'Pansy', 'Tulip', 'Fuchsia', 'Cyclamen', 'Lavender']
# Each data structure is on its own line to help clarify what they are.
>>> nested_data_structures = [
{"fish": "gold", "monkey": "brown", "parrot": "grey"},
("fish", "mammal", "bird"),
['water', 'jungle', 'sky']
]
>>> nested_data_structures
[{'fish': 'gold', 'monkey': 'brown', 'parrot': 'grey'}, ('fish', 'mammal', 'bird'), ['water', 'jungle', 'sky']]
list() 생성자는 비워 두거나 _반복 가능한 객체_를 인자로 넘겨 사용할 수 있어요.
생성자는 반복 가능한 객체의 원소를 차례로 돌면서 list에 순서대로 추가해요:
>>> no_elements = list()
>>> no_elements
[]
# The tuple is unpacked and each element is added.
>>> multiple_elements_from_tuple = list(("Parrot", "Bird", 334782))
>>> multiple_elements_from_tuple
['Parrot', 'Bird', 334782]
# The set is unpacked and each element is added.
>>> multiple_elements_from_set = list({2, 3, 5, 7, 11})
>>> multiple_elements_from_set
[2, 3, 5, 7, 11]
string이나 dict와 함께 list 생성자를 사용했을 때의 결과는 의외일 수 있어요:
# String elements (Unicode code points) are iterated through and added *individually*.
>>> multiple_elements_string = list("Timbuktu")
>>> multiple_elements_string
['T', 'i', 'm', 'b', 'u', 'k', 't', 'u']
# Unicode separators and positioning code points are also added *individually*.
>>> multiple_code_points_string = list('अभ्यास')
>>> multiple_code_points_string
['अ', 'भ', '्', 'य', 'ा', 'स']
# The iteration default for dictionaries is over the keys, so only key data is inserted into the list.
>>> source_data = {"fish": "gold", "monkey": "brown"}
>>> multiple_elements_dict_1 = list(source_data)
['fish', 'monkey']
list 생성자는 _반복 가능한 객체_만(또는 아무것도) 인자로 받기 때문에, 반복할 수 없는 객체를 넘기면 타입 오류가 발생해요.
따라서 원소가 하나인 list는 리터럴 방식으로 만드는 편이 훨씬 쉬워요.
# Numbers are not iterable, and so attempting to create a list with a number passed to the constructor fails.
>>> one_element = list(16)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
# Tuples *are* iterable, so passing a one-element tuple to the constructor does work, but it's awkward
>>> one_element_from_iterable = list((16,))
>>> one_element_from_iterable
[16]
배열 안의 항목은(다른 시퀀스 타입인 str과 tuple의 항목과 마찬가지로) 0-based index와 대괄호 표기법_으로 접근할 수 있어요.
인덱스는 left --> right(0에서 시작) 방향일 수도, right --> left(-1에서 시작_) 방향일 수도 있어요.
| 왼쪽에서의 인덱스 ⟹ |
|
⟸ 오른쪽에서의 인덱스 |
>>> breakfast_foods = ["Oatmeal", "Fruit Salad", "Eggs", "Toast"]
# Oatmeal is at index 0 or index -4.
>>> breakfast_foods[0]
'Oatmeal'
>>> breakfast_foods[-4]
'Oatmeal'
# Eggs are at index -2 or 2
>>> breakfast_foods[-2]
'Eggs'
>>> breakfast_foods[2]
'Eggs'
# Toast is at -1
>>> breakfast_foods[-1]
'Toast'
list 안 원소의 일부분은 슬라이스 표기법(<list>[start:stop])으로 접근할 수 있어요.
_슬라이스_는 start <= index < stop인 위치 index의 원소 시퀀스로 정의돼요.
_슬라이싱_은 잘라낸 항목의 복사본을 반환하며 원본 list는 수정하지 않아요.
[start:stop:step]처럼 step 매개변수를 사용해 배열의 원소를 "건너뛰거나" 걸러낼 수도 있어요(예를 들어 step이 2이면 범위 안에서 하나 걸러 하나씩 원소를 선택해요):
>>> colors = ["Red", "Purple", "Green", "Yellow", "Orange", "Pink", "Blue", "Grey"]
# If there is no step parameter, the step is assumed to be 1.
>>> middle_colors = colors[2:6]
>>> middle_colors
['Green', 'Yellow', 'Orange', 'Pink']
# If the start or stop parameters are omitted, the slice will
# start at index zero, and will stop at the end of the list.
>>> primary_colors = colors[::3]
>>> primary_colors
['Red', 'Yellow', 'Blue']
배열에 내장 sum() 함수를 사용하면 배열 안 모든 숫자의 합을 반환해요:
>>> number_list = [1, 2, 3, 4]
>>> sum(number_list)
10
len() 함수를 사용하면 배열의 _길이_도 구할 수 있어요:
>>> long_list = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]
>>> len(long_list)
10
배열은 여러 가지 방식으로 합칠 수도 있어요:
# Using the plus + operator unpacks each list and creates a new list, but it is not efficient.
>>> new_via_concatenate = ["George", 5] + ["cat", "Tabby"]
>>> new_via_concatenate
['George', 5, 'cat', 'Tabby']
# Likewise, using the multiplication operator * is the equivalent of using + n times.
>>> first_group = ["cat", "dog", "elephant"]
>>> multiplied_group = first_group * 3
>>> multiplied_group
['cat', 'dog', 'elephant', 'cat', 'dog', 'elephant', 'cat', 'dog', 'elephant']
배열은 _반복자_를 제공하며, 다른 _시퀀스 타입_과 같은 방식으로 반복할 수 있어요.
# Looping through the list and printing out each element.
>>> colors = ["Orange", "Green", "Grey", "Blue"]
>>> for item in colors:
... print(item)
...
Orange
Green
Grey
Blue
loops와 iterators에 대한 더 자세한 설명은 loops 개념을 완료해 보세요.
Elyse는 다가오는 라스베이거스 여행에서 포커(그리고 다른 카드 게임)를 할 생각에 무척 기대하고 있어요. '자기 기록'을 좋아하는 팬이라서, 기록 작업에 도움이 될 작은 함수 몇 개를 만들고 싶어 해요. 그래서 그 함수들을 함께 고민해 달라고 도움을 요청했어요.
Elyse는 포커를 특히 좋아해서, 몇 라운드를 플레이했는지, 그리고 그게 _어떤 라운드_인지 기록하고 싶어 해요. 모든 라운드에는 고유한 번호가 있고, 각 테이블에는 현재 플레이 중인 라운드 번호가 표시돼요. Elyse는 테이블 하나를 골라 앉아서 첫 라운드를 시작해요. 세 라운드를 플레이할 계획이에요.
현재 라운드 번호를 받아서, 그 라운드와 앞으로 다가올 _다음 두 라운드_를 담은 list 하나를 반환하는 get_rounds(<round_number>) 함수를 구현해요.
>>> get_rounds(27)
[27, 28, 29]
Elyse는 첫 번째 테이블에서 몇 라운드를 플레이하고, 잠깐 쉬었다가 두 번째 테이블에서 또 몇 라운드를 플레이했어요. 그런데 테이블마다 서로 다른 배열이 생기고 말았어요! 그녀는 두 배열을 하나로 합쳐서 모든 포커 라운드를 같은 곳에서 기록하고 싶어 해요.
배열 두 개를 받아서, 첫 번째 list에 있는 모든 라운드 뒤에 두 번째 list의 모든 라운드를 이어 붙인 list 하나를 반환하는 concatenate_rounds(<rounds_1>, <rounds_2>) 함수를 구현해요.
>>> concatenate_rounds([27, 28, 29], [35, 36])
[27, 28, 29, 35, 36]
이전 포커 라운드 몇 개에 대해 이야기하던 중, 다른 플레이어가 그중 두 라운드가 얼마나 비슷하게 흘러갔는지 언급해요. Elyse는 자신이 그 라운드를 플레이했는지 아닌지 확신하지 못해요.
플레이한 라운드 배열과 라운드 번호, 두 인자를 받는 list_contains_round(<rounds>, <round_number>) 함수를 구현해요. 이 함수는 해당 라운드가 플레이한 라운드 배열에 있으면 True를, 없으면 False를 반환해요.
>>> list_contains_round([27, 28, 29, 35, 36], 29)
True
>>> list_contains_round([27, 28, 29, 35, 36], 30)
False
Elyse는 Black Joe라는 새로운 게임을 해보고 싶어 해요. Black Jack은 손에 든 카드의 합을 목표 값으로 맞추는 게 목표지만, Black Joe는 카드 값의 _평균_이 7이 되는 게 목표예요. 평균은 모든 카드 값을 더한 뒤, 그 합을 손에 든 카드 수로 나누면 돼요.
Black Joe에서 한 패의 카드 값 평균을 반환하는 card_average(<hand>) 함수를 구현해요.
>>> card_average([5, 6, 7])
6.0
Black Joe에서는 속도가 중요해요. Elyse는 평균을 더 빠르게 구할 방법을 찾아보려고 해요.
그녀는 평균 비슷한 값을 얻는 두 가지 방법을 떠올렸어요:
손에 든 카드 값들이 담긴 배열 hand가 주어졌을 때, approx_average_is_average(<hand>) 함수를 구현해요.
위에서 말한 방법 중 하나 or _둘 다_가 _실제 평균_과 같은 값을 내면 True를 반환해요.
참고: 중앙값을 더 쉽게 구할 수 있도록, 모든 패의 카드 수는 홀수예요.
>>> approx_average_is_average([1, 2, 3])
True
>>> approx_average_is_average([2, 3, 4, 8, 8])
True
>>> approx_average_is_average([1, 2, 3, 5, 9])
False
평균 실험 결과에 흥미를 느낀 Elyse는 짝수 위치의 카드 평균과 홀수 위치의 카드 평균이 같은 결과를 내는지 궁금해져요. 이제 또 다른 테스트 함수를 만들 차례예요!
짝수 인덱스에 있는 카드들의 평균이 홀수 인덱스에 있는 카드들의 평균과 같은지를 나타내는 불리언을 반환하는 average_even_is_average_odd(<hand>) 함수를 구현해요.
>>> average_even_is_average_odd([1, 2, 3])
True
>>> average_even_is_average_odd([1, 2, 3, 4])
False
Black Joe에서는 11번째 패마다 보너스 규칙이 적용되는 보너스 패예요. 마지막으로 뽑은 카드가 Jack이면 그 값을 두 배로 만들어요.
한 패를 받아서 마지막 카드가 Jack(11)인지 확인하는 maybe_double_last(<hand>) 함수를 구현해요. 마지막 카드가 Jack(11)이 맞으면, 패를 반환하기 전에 그 값을 두 배로 만들어요.
>>> hand = [5, 9, 11]
>>> maybe_double_last(hand)
[5, 9, 22]
>>> hand = [5, 9, 10]
>>> maybe_double_last(hand)
[5, 9, 10]
Exercism에 가입하고 Python 트랙을 개념 17개연습 문제 146개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.