파이썬 리스트(list): 메서드, 슬라이싱, 정렬, append, 튜플과 집합
파이썬 리스트(list)는 대괄호로 묶은 순서가 있는 변경 가능한 자료형입니다. 예: [250, 1200, 99]. append로 끝에 추가하고, insert와 remove로 넣고 빼며, 슬라이싱으로 일부를 잘라내고, sort로 정렬합니다. sort는 원본을 정렬하고 None을 반환하며, sorted는 정렬된 새 리스트를 반환합니다.
파이썬 리스트(list)는 대괄호로 묶은 값들의 순서 있는 모음이며, 내용을 바꿀 수 있습니다. 끝에 원소를 추가하고, 중간에 끼워 넣고, 지우고, 정렬할 수 있습니다. 파이썬에서 가장 많이 쓰는 컬렉션으로, 파일의 줄, 한 달의 지출, API 응답 등 거의 모든 데이터가 리스트에서 시작합니다. 이 글에서는 리스트를 만드는 법, 메서드, 슬라이싱, 정렬, 리스트 컴프리헨션, 복사를 차례로 살펴보고, 마지막으로 튜플과 집합을 설명합니다.
파이썬 리스트란
리스트는 원소를 넣은 순서대로 보관하고, 인덱스는 0부터 셉니다. 한 리스트에 서로 다른 타입의 값을 넣을 수도 있지만, 실제로는 같은 종류의 데이터를 담는 경우가 대부분입니다.
amounts = [250, 1200, 99]
print(amounts[0]) # 250 — 첫 번째 원소
print(amounts[-1]) # 99 — 마지막 원소
amounts[1] = 1300 # 리스트는 변경할 수 있습니다
print(len(amounts)) # 3 — 길이
리스트의 길이는 len() 함수로 구합니다. 존재하지 않는 인덱스에 접근하면 IndexError: list index out of range가 발생합니다.
리스트 만들기
empty = [] # 빈 리스트
also_empty = list() # list 함수로 만들기
nums = [1, 2, 3]
chars = list("abc") # ['a', 'b', 'c']
digits = list(range(5)) # [0, 1, 2, 3, 4]
zeros = [0] * 4 # [0, 0, 0, 0]
list() 함수는 반복 가능한 객체를 리스트로 바꿔 줍니다. 문자열, range, 집합, 딕셔너리의 키 등이 해당합니다.
append와 다른 리스트 메서드
append는 원소 하나를 리스트 끝에 추가합니다. 파이썬에서 가장 자주 쓰는 메서드입니다. 리스트 자체를 바꾸고 None을 반환합니다.
amounts = [250, 1200]
amounts.append(450) # [250, 1200, 450]
amounts.extend([10, 20]) # [250, 1200, 450, 10, 20]
amounts.insert(0, 5) # [5, 250, 1200, 450, 10, 20]
amounts.remove(1200) # 처음 나오는 값 1200을 삭제
last = amounts.pop() # 20 — 마지막 원소를 삭제하고 반환
흔한 실수가 있습니다. a.append([10, 20])은 숫자 두 개가 아니라 리스트 하나를 원소로 추가합니다. 여러 원소를 한꺼번에 넣으려면 extend를 쓰세요.
| 메서드 | 하는 일 |
|---|---|
append(x) | x를 끝에 추가 |
extend(iter) | 시퀀스의 모든 원소를 추가 |
insert(i, x) | 인덱스 i 앞에 x 삽입 |
remove(x) | 처음 나오는 x를 삭제 (없으면 ValueError) |
pop(i=-1) | 인덱스의 원소를 삭제하고 반환 |
index(x) | 처음 나오는 x의 인덱스 |
count(x) | x가 나온 횟수 |
sort() | 제자리에서 정렬 |
reverse() | 제자리에서 뒤집기 |
copy() | 얕은 복사 |
clear() | 모두 비우기 |
인덱스로 원소를 지우거나 범위를 지울 때는 del 문도 씁니다: del a[0], del a[1:3]. 리스트와 함께 자주 쓰는 내장 함수는 len, sum, min, max, sorted, reversed, enumerate, zip입니다.
파이썬 슬라이싱
슬라이스 a[start:stop:step]은 리스트의 일부를 가져옵니다. start는 포함하고, stop은 포함하지 않으며, step만큼 건너뜁니다. 생략할 수 있는 부분은 생략하면 됩니다. 슬라이스는 항상 새 리스트를 반환합니다.
a = [0, 1, 2, 3, 4, 5]
print(a[1:4]) # [1, 2, 3]
print(a[:2]) # [0, 1] — 앞의 두 개
print(a[-2:]) # [4, 5] — 뒤의 두 개
print(a[::2]) # [0, 2, 4] — 두 칸씩
print(a[::-1]) # [5, 4, 3, 2, 1, 0] — 뒤집은 리스트
print(a[:]) # 전체 복사본
인덱스와 달리, 리스트 범위를 벗어난 슬라이스는 오류를 내지 않습니다. a[10:20]은 그냥 []를 반환합니다. 슬라이스에 값을 대입해 일부를 바꿀 수도 있습니다: a[1:3] = ["x", "y", "z"].
문자열 슬라이싱
슬라이싱은 문자열과 튜플 같은 모든 시퀀스에서 같은 방식으로 동작합니다.
s = "Python 3.15"
print(s[:6]) # Python
print(s[-4:]) # 3.15
print(s[::-1]) # 51.3 nohtyP
문자열은 슬라이스로 바꿀 수 없습니다. 문자열은 변경할 수 없으므로 슬라이스는 새 문자열을 만듭니다.
리스트 정렬: sort와 sorted
초보자가 가장 많이 헷갈리는 부분입니다. sort 메서드는 리스트 안의 원소를 바로 재배열하고 None을 반환합니다. sorted 함수는 원본을 건드리지 않고 정렬된 새 리스트를 반환합니다.
amounts = [250, 1200, 99]
amounts.append(450)
print(sorted(amounts)) # [99, 250, 450, 1200] — 새 복사본
print(amounts) # [250, 1200, 99, 450] — 원본은 그대로
result = amounts.sort() # 제자리에서 정렬
print(result, amounts) # None [99, 250, 450, 1200]
그래서 '왜 None이 나오지?'라는 고전적인 문제가 생깁니다. a = a.sort()라고 쓰면 리스트가 None으로 덮어씌워집니다. a.sort()로 쓰거나 a = sorted(a)로 쓰세요.
key 인수와 내림차순
두 방식 모두 key와 reverse 인수를 받습니다. key는 원소를 비교할 기준 값을 계산하는 함수입니다.
words = ["커피", "택시요금", "구독"]
print(sorted(words, key=len)) # ['커피', '구독', '택시요금']
print(sorted(["bob", "Alice", "carl"], key=str.lower)) # ['Alice', 'bob', 'carl']
print(sorted([3, 1, 2], reverse=True)) # [3, 2, 1]
expenses = [("식비", 650), ("영화", 120), ("교통", 300)]
expenses.sort(key=lambda e: e[1]) # 금액 기준
파이썬의 정렬은 안정적(stable)입니다. 키가 같은 원소는 원래 순서를 유지합니다. 차이는 적용 범위에도 있습니다. sorted는 문자열, 딕셔너리, 집합 등 모든 반복 가능한 객체에 쓸 수 있지만, sort 메서드는 리스트에만 있습니다.
리스트 뒤집기
뒤집는 방법은 세 가지이며, 동작이 서로 다릅니다.
a.reverse()— 제자리에서 뒤집고None을 반환합니다.a[::-1]— 뒤집은 새 리스트를 만듭니다.reversed(a)— 필요할 때 값을 만드는 이터레이터입니다. 반복문에서 씁니다:for x in reversed(a):.
리스트 순회
items = ["커피", "지하철", "영화"]
for item in items:
print(item)
for i, item in enumerate(items, start=1):
print(i, item) # 1 커피, 2 지하철, 3 영화
print("영화" in items) # True — 포함 여부 확인
순회 중인 리스트에서 원소를 지우면 일부가 건너뛰어집니다. 리스트 컴프리헨션으로 새 리스트를 만들거나, 복사본 a[:]를 순회하세요.
리스트 컴프리헨션
리스트 컴프리헨션은 한 줄로 새 리스트를 만듭니다. 표현식, 반복문, 그리고 선택적인 조건으로 구성됩니다.
squares = [n * n for n in range(6)] # [0, 1, 4, 9, 16, 25]
big = [a for a in [250, 1200, 99] if a > 200] # [250, 1200]
names = [s.strip() for s in [" 안나 ", "올레"]] # ['안나', '올레']
컴프리헨션은 보통 append를 쓰는 반복문보다 빠르고 읽기 쉽습니다. 다만 로직이 한 줄로 이해하기 어렵다면, 평범한 반복문이 더 낫습니다.
리스트 합치기, 더하기, 빼기
a = [1, 2, 3]
b = [3, 4]
print(a + b) # [1, 2, 3, 3, 4] — 새 리스트
a += b # a.extend(b)와 같습니다
print([*a, *b]) # 언패킹으로 새 리스트 만들기
리스트끼리 - 연산자로 뺄 수는 없습니다. 한 리스트에서 다른 리스트의 원소를 제외하려면 컴프리헨션을 쓰세요: [x for x in a if x not in set(b)]. 교집합은 집합으로 구하는 것이 편합니다: set(a) & set(b). 다만 이 방법은 순서와 중복이 사라집니다.
중첩 리스트와 2차원 리스트
리스트의 리스트는 표나 행렬을 저장하는 간단한 방법입니다.
matrix = [[1, 2, 3], [4, 5, 6]]
print(matrix[1][2]) # 6
grid = [[0] * 3 for _ in range(2)] # 올바른 방법
bad = [[0] * 3] * 2 # 함정: 같은 리스트를 두 번 참조
bad[0][0] = 9
print(bad) # [[9, 0, 0], [9, 0, 0]]
리스트 언패킹
first, second, third = [10, 20, 30]
head, *rest = [1, 2, 3, 4] # head=1, rest=[2, 3, 4]
print(*["a", "b"], sep=", ") # a, b — 대괄호 없이 출력
변수 개수가 리스트 길이와 맞지 않으면 ValueError가 납니다. 이때 별표(*)가 도움이 됩니다.
리스트 복사
b = a는 리스트를 복사하지 않고, 같은 객체에 이름을 하나 더 붙일 뿐입니다. 그래서 b로 바꾸면 a에도 보입니다. 독립된 복사본은 a.copy(), list(a), a[:]로 명시적으로 만듭니다. 중첩된 데이터에서는 이것으로 부족합니다. 안쪽 리스트는 여전히 공유되므로 copy.deepcopy(a)가 필요합니다.
import copy
a = [[1, 2], [3]]
b = a.copy()
b[0].append(99)
print(a) # [[1, 2, 99], [3]] — 안쪽 리스트는 공유됩니다
c = copy.deepcopy(a) # 완전히 독립된 복사본
튜플: 변경할 수 없는 리스트
튜플(tuple)은 리스트와 비슷하지만 만든 뒤에는 바꿀 수 없습니다. 고정된 필드를 가진 하나의 기록이지, 계속 늘어나는 목록이 아니라는 신호를 읽는 사람에게 줍니다. 튜플은 간편하게 언패킹되며, 함수가 여러 값을 반환할 때 흔히 사용합니다.
expense = (250, "커피")
amount, item = expense
expense[0] = 300
# TypeError: 'tuple' object does not support item assignment
변경할 수 없는 값으로만 이루어진 튜플은 해시 가능하므로 딕셔너리의 키로 쓸 수 있습니다. 원소가 하나인 튜플은 쉼표를 붙여 씁니다: (5,).
집합: 중복 없는 값
집합(set)은 고유한 값만 저장하며 순서를 기억하지 않습니다. 중복을 없애거나 '이 값이 있는가'를 확인하는 가장 빠른 방법이고, 교집합, 합집합, 차집합도 지원합니다.
jan = {"식비", "택시", "영화"}
feb = {"식비", "구독"}
print(jan & feb) # {'식비'}
print(feb - jan) # {'구독'}
unique = list(dict.fromkeys([3, 1, 3, 2])) # [3, 1, 2] — 중복 제거, 순서 유지
empty = set() # {} 는 빈 딕셔너리입니다!
어떤 컬렉션을 고를까
| 용도 | 컬렉션 |
|---|---|
| 순서가 있고 내용을 바꿔야 함 | list |
| 고정된 기록 | tuple |
| 키로 찾기 | dict |
| 중복 없음, '포함 여부' 확인 | set |
| 변경할 수 없는 설정 | frozendict (Python 3.15) |
x in list는 원소를 하나씩 비교하지만, x in set은 거의 즉시 끝납니다. 반복문 안에서 큰 리스트에 포함 여부를 여러 번 확인해야 한다면 집합으로 바꾸세요.
정리
파이썬 리스트는 순서가 있고 변경할 수 있는 컬렉션입니다. append와 extend로 추가하고, pop과 remove로 삭제하고, a[start:stop:step] 슬라이스로 일부를 가져옵니다. sort는 제자리에서 정렬하고 None을 반환하며, sorted는 새 리스트를 반환합니다. 복사는 명시적으로 하고, 고정된 기록에는 튜플을, 중복 없는 값에는 집합을 쓰세요. 리스트, 튜플, 딕셔너리, 집합은 '파이썬 3.15: 초보부터 전문가까지' 과정에서 실습으로 다룹니다.
자주 묻는 질문
파이썬의 리스트는 다른 언어의 배열과 어떻게 다른가요?
파이썬에는 다른 언어와 같은 의미의 기본 배열이 없고, 그 역할을 리스트가 합니다. 리스트에는 서로 다른 타입의 값을 넣을 수 있습니다. 숫자 배열이 필요하면 array 모듈이나 NumPy를 씁니다.
리스트에서 원소의 인덱스는 어떻게 찾나요?
a.index(x)는 처음 나오는 위치를 반환하고, 원소가 없으면 ValueError를 냅니다. 호출하기 전에 x in a로 확인하세요.
순서를 유지하면서 리스트의 중복을 없애려면?
list(dict.fromkeys(a))를 쓰세요. 딕셔너리는 키를 중복 없이 삽입 순서대로 저장합니다. list(set(a))도 중복은 없애지만 순서는 유지하지 않습니다.
문자열 리스트를 한 문자열로 합치려면?
문자열의 join 메서드를 씁니다. ", ".join(["a", "b", "c"])는 "a, b, c"를 만듭니다. 모든 원소가 문자열이어야 하므로, 숫자는 map(str, a)로 먼저 변환하세요.
리스트 하나에 서로 다른 타입의 값을 넣어도 되나요?
네. 숫자, 문자열, 다른 리스트, 어떤 객체든 한 리스트에 함께 넣을 수 있습니다. 다만 key 없이 이런 리스트를 정렬하면 숫자와 문자열을 비교하게 되어 TypeError가 납니다.