2026. 6. 12. 16:57ㆍDeveloper/Python
리스트는 Python에서 가장 많이 쓰는 자료구조입니다. 여러 값을 순서대로 담아두고, 추가하고, 꺼내고, 정렬하는 모든 작업이 리스트로 시작합니다.
핵심 요약
- 리스트는 여러 값을 순서대로 담는 자료구조입니다.
[1, 2, 3]처럼 대괄호로 만듭니다. - 인덱스는 0부터 시작하고, 음수 인덱스(
-1)로 뒤에서부터 접근할 수 있습니다. - 슬라이싱(
[start:end:step])으로 리스트의 일부분을 잘라낼 수 있습니다. append(),insert(),remove(),sort()등 메서드로 리스트를 조작합니다.- 리스트 컴프리헨션(
[x for x in ...])은 반복문을 한 줄로 줄여주는 Python 고유 문법입니다. append()는 빠르지만(O(1)),insert(0, x)는 느립니다(O(n)). 차이를 알면 성능 문제를 피할 수 있습니다.
1. 리스트 만들기
대괄호로 생성
numbers = [1, 2, 3, 4, 5]
fruits = ["사과", "바나나", "딸기"]
empty = [] # 빈 리스트
서로 다른 타입도 섞을 수 있다
mixed = [1, "hello", 3.14, True, None]
Python 리스트는 타입 제한이 없습니다. 다만 실무에서는 같은 타입의 값을 모아두는 경우가 대부분입니다.
리스트 안에 리스트 (중첩)
matrix = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
print(matrix[0]) # [1, 2, 3] — 첫 번째 행
print(matrix[1][2]) # 6 — 두 번째 행의 세 번째 값
다른 방법으로 만들기
# list()로 변환
chars = list("hello") # ['h', 'e', 'l', 'l', 'o']
nums = list(range(5)) # [0, 1, 2, 3, 4]
# 반복으로 초기화
zeros = [0] * 10 # [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
2. 인덱싱 — 값 꺼내기
리스트에서 하나의 값을 꺼낼 때 인덱스를 사용합니다. 인덱스는 0부터 시작합니다.
fruits = ["사과", "바나나", "딸기", "포도", "수박"]
# 0 1 2 3 4
# -5 -4 -3 -2 -1
print(fruits[0]) # "사과" — 첫 번째
print(fruits[2]) # "딸기" — 세 번째
print(fruits[-1]) # "수박" — 마지막
print(fruits[-2]) # "포도" — 뒤에서 두 번째
값 변경하기
리스트는 변경 가능(mutable)합니다. 인덱스로 특정 위치의 값을 바꿀 수 있습니다.
fruits[1] = "망고"
print(fruits) # ["사과", "망고", "딸기", "포도", "수박"]
범위를 벗어나면 에러
fruits = ["사과", "바나나", "딸기"]
# fruits[5] # IndexError: list index out of range
3. 슬라이싱 — 범위로 잘라내기
슬라이싱은 리스트의 일부분을 새 리스트로 가져오는 문법입니다.
리스트[start:end] # start부터 end-1까지
리스트[start:end:step] # step 간격으로
기본 슬라이싱
nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(nums[2:5]) # [2, 3, 4] — 2번째~4번째 (5번째 미포함)
print(nums[:3]) # [0, 1, 2] — 처음부터 2번째까지
print(nums[7:]) # [7, 8, 9] — 7번째부터 끝까지
print(nums[:]) # [0, 1, 2, ..., 9] — 전체 복사
step 사용
nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(nums[::2]) # [0, 2, 4, 6, 8] — 2칸씩 건너뛰기
print(nums[1::2]) # [1, 3, 5, 7, 9] — 홀수 인덱스만
print(nums[::-1]) # [9, 8, 7, ..., 0] — 뒤집기
슬라이싱으로 값 변경
nums = [0, 1, 2, 3, 4]
nums[1:3] = [10, 20] # 1~2번째를 교체
print(nums) # [0, 10, 20, 3, 4]
nums[1:3] = [10, 20, 30, 40] # 길이가 달라도 됨
print(nums) # [0, 10, 20, 30, 40, 3, 4]
슬라이싱은 새 리스트를 만든다
original = [1, 2, 3, 4, 5]
sliced = original[1:4] # [2, 3, 4] — 새로운 리스트
sliced[0] = 99
print(original) # [1, 2, 3, 4, 5] — 원본은 변하지 않음
4. 주요 메서드
추가하기
fruits = ["사과", "바나나"]
# 맨 뒤에 추가
fruits.append("딸기")
print(fruits) # ["사과", "바나나", "딸기"]
# 특정 위치에 삽입
fruits.insert(1, "망고")
print(fruits) # ["사과", "망고", "바나나", "딸기"]
# 여러 개를 한번에 추가
fruits.extend(["포도", "수박"])
print(fruits) # ["사과", "망고", "바나나", "딸기", "포도", "수박"]
삭제하기
fruits = ["사과", "바나나", "딸기", "바나나"]
# 값으로 삭제 (첫 번째 일치 항목만)
fruits.remove("바나나")
print(fruits) # ["사과", "딸기", "바나나"]
# 인덱스로 삭제 (삭제된 값을 반환)
removed = fruits.pop(1)
print(removed) # "딸기"
print(fruits) # ["사과", "바나나"]
# 마지막 값 삭제
last = fruits.pop()
print(last) # "바나나"
print(fruits) # ["사과"]
# 전체 삭제
fruits.clear()
print(fruits) # []
정렬하기
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
# 오름차순 정렬 (원본을 변경)
numbers.sort()
print(numbers) # [1, 1, 2, 3, 4, 5, 6, 9]
# 내림차순 정렬
numbers.sort(reverse=True)
print(numbers) # [9, 6, 5, 4, 3, 2, 1, 1]
# 원본을 유지하고 새 리스트를 만들고 싶으면 sorted() 사용
original = [3, 1, 4, 1, 5]
new_list = sorted(original)
print(original) # [3, 1, 4, 1, 5] — 원본 유지
print(new_list) # [1, 1, 3, 4, 5]
찾기와 세기
fruits = ["사과", "바나나", "딸기", "바나나"]
# 위치 찾기
print(fruits.index("딸기")) # 2
# fruits.index("포도") # ValueError — 없으면 에러
# 개수 세기
print(fruits.count("바나나")) # 2
# 존재 여부 확인 (in 연산자)
print("딸기" in fruits) # True
print("포도" in fruits) # False
뒤집기
nums = [1, 2, 3, 4, 5]
nums.reverse()
print(nums) # [5, 4, 3, 2, 1]
메서드 요약표
| 메서드 | 동작 | 반환값 |
|---|---|---|
append(x) |
맨 뒤에 x 추가 | None (원본 변경) |
insert(i, x) |
i번째에 x 삽입 | None (원본 변경) |
extend(iterable) |
여러 값을 뒤에 추가 | None (원본 변경) |
remove(x) |
첫 번째 x 삭제 | None (원본 변경) |
pop(i) |
i번째 삭제 후 반환 | 삭제된 값 |
pop() |
마지막 삭제 후 반환 | 삭제된 값 |
clear() |
전체 삭제 | None |
sort() |
오름차순 정렬 | None (원본 변경) |
reverse() |
순서 뒤집기 | None (원본 변경) |
index(x) |
x의 위치 반환 | 인덱스 (없으면 에러) |
count(x) |
x의 개수 반환 | 정수 |
copy() |
얕은 복사본 반환 | 새 리스트 |
5. 리스트 컴프리헨션
반복문으로 리스트를 만드는 작업을 한 줄로 줄여주는 Python 문법입니다.
기본 형태
# 반복문 방식
squares = []
for x in range(5):
squares.append(x ** 2)
# [0, 1, 4, 9, 16]
# 컴프리헨션 (같은 결과, 한 줄)
squares = [x ** 2 for x in range(5)]
# [0, 1, 4, 9, 16]
조건 필터링
# 짝수만 골라내기
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = [n for n in numbers if n % 2 == 0]
# [2, 4, 6, 8, 10]
# 양수만 제곱
values = [-3, -1, 0, 2, 5]
positive_squares = [x ** 2 for x in values if x > 0]
# [4, 25]
변환에 활용
# 문자열 리스트를 대문자로
names = ["alice", "bob", "charlie"]
upper_names = [name.upper() for name in names]
# ["ALICE", "BOB", "CHARLIE"]
# 문자열 → 정수 변환
str_nums = ["1", "2", "3", "4"]
int_nums = [int(s) for s in str_nums]
# [1, 2, 3, 4]
언제 컴프리헨션을 쓰고, 언제 for문을 쓰는가
| 상황 | 권장 |
|---|---|
| 단순 변환/필터링 | 컴프리헨션 |
| 조건이 복잡하거나 여러 줄이 필요 | for문 |
| 결과 리스트가 필요 없고 부수 효과만 원할 때 | for문 |
# 컴프리헨션이 적합
doubled = [x * 2 for x in range(10)]
# for문이 적합 (복잡한 로직)
results = []
for item in data:
if item.is_valid():
processed = item.transform()
if processed.score > 0.5:
results.append(processed)
한 줄이 너무 길어지면(80자 이상) for문이 읽기 쉽습니다.
6. 리스트 복사
얕은 복사 (shallow copy)
original = [1, 2, 3]
# 방법 1: 슬라이싱
copy1 = original[:]
# 방법 2: copy() 메서드
copy2 = original.copy()
# 방법 3: list() 생성자
copy3 = list(original)
# 복사본을 변경해도 원본은 안전
copy1[0] = 99
print(original) # [1, 2, 3]
주의: 중첩 리스트는 얕은 복사로 부족
matrix = [[1, 2], [3, 4]]
shallow = matrix[:]
shallow[0][0] = 99
print(matrix) # [[99, 2], [3, 4]] — 원본도 변경됨!
얕은 복사는 바깥 리스트만 복사하고, 안쪽 리스트는 같은 객체를 공유합니다. 중첩된 구조까지 완전히 복사하려면 깊은 복사가 필요합니다.
import copy
matrix = [[1, 2], [3, 4]]
deep = copy.deepcopy(matrix)
deep[0][0] = 99
print(matrix) # [[1, 2], [3, 4]] — 원본 안전
7. 유용한 내장 함수들
리스트와 함께 자주 쓰이는 내장 함수입니다.
nums = [3, 1, 4, 1, 5, 9]
len(nums) # 6 — 길이
sum(nums) # 23 — 합계
min(nums) # 1 — 최솟값
max(nums) # 9 — 최댓값
sorted(nums) # [1, 1, 3, 4, 5, 9] — 정렬된 새 리스트 (원본 유지)
enumerate — 인덱스와 값을 함께
fruits = ["사과", "바나나", "딸기"]
for i, fruit in enumerate(fruits):
print(f"{i}번: {fruit}")
# 0번: 사과
# 1번: 바나나
# 2번: 딸기
enumerate()를 쓰면 인덱스 변수를 따로 관리할 필요가 없습니다.
zip — 여러 리스트를 묶기
names = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]
for name, score in zip(names, scores):
print(f"{name}: {score}점")
# Alice: 85점
# Bob: 92점
# Charlie: 78점
8. 시간 복잡도 — 어떤 작업이 빠르고 느린가
리스트의 각 연산이 얼마나 빠른지 알아두면, 데이터가 많아졌을 때 느려지는 코드를 피할 수 있습니다.
| 연산 | 시간 복잡도 | 의미 |
|---|---|---|
lst[i] |
O(1) | 인덱스 접근은 항상 빠름 |
lst.append(x) |
O(1) | 맨 뒤에 추가는 빠름 |
lst.pop() |
O(1) | 맨 뒤에서 삭제는 빠름 |
lst.pop(0) |
O(n) | 맨 앞에서 삭제는 느림 (뒤 요소를 모두 이동) |
lst.insert(0, x) |
O(n) | 맨 앞에 삽입도 느림 |
x in lst |
O(n) | 처음부터 끝까지 찾아야 할 수 있음 |
lst.sort() |
O(n log n) | 정렬은 중간 정도 |
lst[i:j] |
O(j-i) | 잘라내는 크기만큼 |
len(lst) |
O(1) | 길이 확인은 항상 빠름 |
O(1)과 O(n)의 차이
- O(1): 리스트에 100만 개가 있어도 같은 시간이 걸림
- O(n): 리스트에 100만 개가 있으면 최대 100만 번 작업해야 함
실무에서 주의할 패턴
# 느린 패턴 — 맨 앞에 계속 삽입 (매번 전체를 이동)
lst = []
for i in range(10000):
lst.insert(0, i) # O(n) × n번 = O(n²)
# 빠른 패턴 — 뒤에 추가하고 나중에 뒤집기
lst = []
for i in range(10000):
lst.append(i) # O(1) × n번 = O(n)
lst.reverse()
맨 앞에서 자주 추가/삭제해야 한다면 collections.deque를 사용하는 것이 더 적합합니다.
9. 자주 하는 실수
실수 1: sort()와 sorted() 혼동
numbers = [3, 1, 4, 1, 5]
# sort()는 None을 반환 — 이렇게 쓰면 None이 됨!
result = numbers.sort()
print(result) # None
# sorted()는 새 리스트를 반환
result = sorted(numbers)
print(result) # [1, 1, 3, 4, 5]
sort()는 원본을 변경하고 None을 반환합니다. 새 리스트가 필요하면 sorted()를 사용합니다.
실수 2: 반복 중 리스트 수정
# 위험! 반복 중 삭제하면 인덱스가 밀림
nums = [1, 2, 3, 4, 5]
for n in nums:
if n % 2 == 0:
nums.remove(n)
print(nums) # [1, 3, 5]가 아닐 수 있음!
# 안전한 방법: 컴프리헨션으로 새 리스트 생성
nums = [1, 2, 3, 4, 5]
odds = [n for n in nums if n % 2 != 0]
print(odds) # [1, 3, 5]
실수 3: 리스트 곱셈으로 중첩 리스트 만들기
# 위험! 같은 리스트 객체가 3번 반복됨
matrix = [[0] * 3] * 3
matrix[0][0] = 1
print(matrix) # [[1, 0, 0], [1, 0, 0], [1, 0, 0]] — 전부 바뀜!
# 안전한 방법
matrix = [[0] * 3 for _ in range(3)]
matrix[0][0] = 1
print(matrix) # [[1, 0, 0], [0, 0, 0], [0, 0, 0]]
실수 4: 빈 리스트 체크를 길게 쓰기
# 불필요하게 긴 표현
if len(items) == 0:
print("비어 있음")
# Python다운 표현
if not items:
print("비어 있음")
10. 정리
| 항목 | 핵심 |
|---|---|
| 생성 | [1, 2, 3], list(), [0] * n |
| 인덱싱 | 0부터 시작, 음수는 뒤에서부터 |
| 슬라이싱 | [start:end:step], 새 리스트 반환 |
| 추가 | append() 빠름, insert(0, x) 느림 |
| 삭제 | pop() 빠름, pop(0) 느림 |
| 정렬 | sort() 원본 변경, sorted() 새 리스트 |
| 컴프리헨션 | [x for x in ... if ...] — 간결한 리스트 생성 |
| 복사 | 얕은 복사 [:], 깊은 복사 copy.deepcopy() |
다음 글에서는 Python 딕셔너리를 다룹니다. 키-값 쌍으로 데이터를 관리하는 방법과, 언제 리스트 대신 딕셔너리를 쓰는지 정리합니다.
관련 글
'Developer > Python' 카테고리의 다른 글
| Python 변수와 데이터 타입 완전 정리: int, float, str, bool, None (0) | 2026.06.12 |
|---|