list는 항목을 순서대로 담는 가변 컬렉션이에요. 대부분의 컬렉션(내장 타입인 tuple, dict, set을 참고해요)처럼, 배열은 여러 데이터 타입(또는 여러 개)에 대한 참조를 담을 수 있어요. 다른 배열도 포함해서요. Python에서는 시퀀스로 취급되며, 슬라이스 표기법으로 전체나 일부를 복사할 수 있어요. 다른 시퀀스와 마찬가지로 lists 안의 원소는 왼쪽에서부터 0-based index 번호로, 또는 오른쪽에서부터 -1-based index 번호로 참조할 수 있어요.
배열은 min(<list>)/max(<list>), <list>.index(), <list>.append(), <list>.reverse() 같은 공통 시퀀스 연산과 가변 시퀀스 연산을 모두 지원해요. for item in <list> 구문으로 항목을 반복할 수 있고, 원소와 원소의 인덱스가 모두 필요할 때는 for index, item in enumerate(<list>)를 사용해요.
Python은 배열을 다루는 데 유용한 메서드를 많이 제공해요. 배열은 가변이기 때문에, 배열 메서드는 메서드에 전달된 원래 배열 객체를 변경해요. 원본을 변경하고 싶지 않다면, slice나 <list>.copy()로 원래 list의 shallow copy(최소한)를 만들어야 해요.
기존 배열의 끝에 항목을 추가하려면 <list>.append(<item>)를 사용해요:
>>> numbers = [1, 2, 3]
>>> numbers.append(9)
>>> numbers
[1, 2, 3, 9]
덧붙이는 대신, <list>.insert(<index>, <item>)는 배열 안 _특정 인덱스_에 항목을 추가해요. <index>는 새 항목이 그 앞에 나타나기를 원하는 항목의 인덱스예요. <item>은 삽입할 원소예요.
참고: <index>가 0이면 항목은 배열의 맨 앞에 추가돼요. <index>가 배열의 마지막 인덱스보다 크면 항목은 맨 끝에 추가돼요. <list>.append(<item>)를 사용하는 것과 같아요.
>>> numbers = [1, 2, 3]
>>> numbers.insert(0, -2)
>>> numbers
[-2, 1, 2, 3]
>>> numbers.insert(1, 0)
>>> numbers
[-2, 0, 1, 2, 3]
iterable은 <list>.extend(<iterable>)로 기존 배열과 합칠 수 있어요(둘을 이어 붙이는 거예요). <list>.extend(<iterable>)는 전달받은 이터러블을 풀어서, 그 원소들을 같은 순서로 대상 배열의 끝에 추가해요. 이 경우 <list>.append(<item>)를 사용하면 이터러블 전체가 _하나의 항목_으로 추가돼요.
>>> numbers = [1, 2, 3]
>>> other_numbers = [5, 6, 7]
>>> numbers.extend(other_numbers)
>>> numbers
[1, 2, 3, 5, 6, 7]
>>> numbers.extend([8, 9])
>>> numbers
[1, 2, 3, 5, 6, 7, 8, 9]
<list>.remove(<item>)로 배열에서 원소를 제거할 수 있어요. <list>.remove(<item>)는 원소가 배열에 없으면 ValueError를 발생시켜요.
>>> numbers = [1, 2, 3]
>>> numbers.remove(2)
>>> numbers
[1, 3]
# Trying to remove a value that is not in the list throws a ValueError.
>>> numbers.remove(0)
ValueError: list.remove(x): x not in list
또는 <list>.pop(<index>) 메서드를 사용하면 원소를 제거하면서 동시에 사용할 수 있도록 return해요. <list>.pop(<index>)는 선택적 매개변수 하나를 받아요. 제거하고 반환할 원소의 index예요. 선택적 <index> 인자를 지정하지 않으면 배열의 마지막 원소가 제거되고 반환돼요. <index>가 배열의 마지막 인덱스보다 큰 수면 IndexError가 발생해요.
>>> numbers = [1, 2, 3]
>>> numbers.pop(0)
1
>>> numbers
[2, 3]
>>> numbers.pop()
3
>>> numbers
[2]
# This will throw an error because there is only index 0.
>>> numbers.pop(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: pop index out of range
<list>.clear()로 list의 모든 항목을 제거할 수 있어요. 매개변수를 받지 않아요.
>>> numbers = [1, 2, 3]
>>> numbers.clear()
>>> numbers
[]
배열 원소의 순서는 <list>.reverse()로 제자리에서 뒤집을 수 있어요. 이 메서드는 원래 배열을 변경해요.
>>> numbers = [1, 2, 3]
>>> numbers.reverse()
>>> numbers
[3, 2, 1]
배열은 <list>.sort()를 사용해 제자리에서 재정렬할 수 있어요. 기본 정렬 순서는 왼쪽에서부터 _오름차순_이에요. Python 문서에는 정렬을 위한 추가 팁과 기법이 있어요.
2002년부터 2022년까지 Python은 내부적으로 Timsort라는 알고리즘으로 배열을 정렬했지만, Python 3.11부터는 Powersort로 바꿨어요.
변경에 관한 Python 코어 팀의 자세한 논의는 GitHub 이슈 78742에서 읽을 수 있어요.
알고리즘의 기술적 세부 사항은 J. Ian Munro와 Sebastian Wild의 논문 거의 최적의 병합 정렬: 기존 런에 최적으로 적응하는 빠르고 실용적인 정렬 방법을 참고해요.
>>> names = ["Tony", "Natasha", "Thor", "Bruce"]
# The default sort order is *ascending*.
>>> names.sort()
>>> names
["Bruce", "Natasha", "Thor", "Tony"]
배열을 _내림차순_으로 정렬하려면 메서드에 reverse=True 인자를 전달해요:
>>> names = ["Tony", "Natasha", "Thor", "Bruce"]
>>> names.sort(reverse=True)
>>> names
["Tony", "Thor", "Natasha", "Bruce"]
원래 list를 변경하고 싶지 않은 경우에는 내장 함수 sorted()와 reversed()를 사용할 수 있어요. sorted(<list>)는 정렬된 _복사본_을 반환하고, <list>.sort()와 같은 매개변수를 받아요. reversed(<list>)는 배열의 항목을 역순으로 내놓는 _이터레이터_를 반환해요. Iterators는 다른 연습 문제에서 자세히 다뤄요.
배열에서 원소가 나타나는 횟수를 구하려면 <list>.count(<item>)를 사용하면 돼요. 이 메서드는 <item>이 배열에 원소로 나타나는 총 횟수를 반환해요.
>>> items = [1, 4, 7, 8, 2, 9, 2, 1, 1, 0, 4, 3]
>>> items.count(1)
3
<list>.index(<item>)는 전달된 항목이 처음 나타나는 위치의 인덱스 번호를 반환해요. 나타나는 곳이 없으면 ValueError가 발생해요. 인덱싱은 왼쪽에서부터 0-based이고, 첫 번째 항목의 위치는 인덱스 0이에요. 오른쪽에서부터의 인덱싱도 지원되는데, 인덱스 -1부터 시작해요.
>>> items = [7, 4, 1, 0, 2, 5]
>>> items.index(4)
1
>>> items.index(10)
ValueError: 10 is not in list
start와 end 인덱스를 지정하면 배열의 특정 구간 안에서 검색해요:
>>> names = ["Tina", "Leo", "Thomas", "Tina", "Emily", "Justin"]
>>> names.index("Tina")
0
>>> names.index("Tina", 2, 5)
3
원소의 정확한 위치가 필요하지 않다면, 내장 in 연산자가 포함 여부를 확인하는 데 더 효율적이에요.
>>> names = ["Tina", "Leo", "Thomas", "Tina", "Emily", "Justin"]
>>> "Thomas" in names
True
Python의 변수는 그 아래에 있는 객체를 가리키는 이름이라는 점을 기억해요. 이름은 프로그램이 실행되는 동안 다른 객체에 묶이거나 다시 묶일 수 있어요. list 객체를 새로운 변수 _이름_에 할당해도 객체나 그것이 참조하는 데이터는 복사되지 않아요. 새 이름과 예전 이름은 모두 같은 list 객체를 가리켜요. 게다가 배열은 컨테이너 타입 객체예요. 공간을 절약하기 위해 컨테이너는 항목 자체가 아니라 구성 항목에 대한 _참조_만 담아요. 이런 "최적화"는 방심한 사람에게 뜻하지 않은 결과를 낳을 수 있어요.
<list>.copy()는 새로운 list 객체를 만들지만, 참조된 배열 _원소_에 대해서는 새 객체를 만들지 않아요. 복사본은 "얕은" 복사예요. shallow copy는 한 list 객체에서 항목을 추가하거나 제거할 때 다른 객체를 수정하지 않으려는 경우 보통 충분해요. 하지만 list의 _내부 원소_가 실수로 변경될(그렇게 해서 관련된 모든 얕은 복사본이 함께 변경될) 가능성이 조금이라도 있다면, copy 모듈의 copy.deepcopy()를 사용해 모든 참조와 객체의 완전한, 즉 "깊은" 복사본을 만들어야 해요.
이름, 값, 배열, 중첩 배열의 동작에 대한 자세한 설명은 Ned Batchelder의 훌륭한 블로그 글 이름과 값: 게임 보드 만들기를 참고해요. Python 객체의 얕은 복사와 깊은 복사도 복사에 관한 고려 사항을 잘 정리해 놓았어요.