파이썬(Python) 리스트 가이드: 필수 문법과 활용법
파이썬 리스트란?
파이썬 리스트(List)는 가장 기본적이면서도 강력한 데이터 구조입니다.
리스트는 여러 항목을 순서대로 저장할 수 있는 컨테이너로,
다양한 타입의 데이터를 함께 담을 수 있습니다.
파이썬 프로그래밍에서 가장 많이 사용되는 자료형 중 하나이며,
데이터 분석, 웹 개발, 인공지능 등 모든 분야에서 필수적으로 활용됩니다.
리스트의 주요 특징은 다음과 같습니다:
- 순서가 있는 데이터 집합
- 인덱스로 각 요소에 접근 가능 (0부터 시작)
- 다양한 타입의 데이터를 함께 저장 가능
- 크기가 가변적 (요소 추가/삭제 가능)
- 중첩 리스트 생성 가능 (리스트 안에 리스트)
리스트 생성하기
파이썬에서 리스트를 생성하는 방법은 여러 가지가 있습니다.
1. 대괄호를 사용한 리스트 생성
가장 기본적인 방법은 대괄호([])를 사용하는 것입니다:
<!-- 기본 리스트 생성 -->
# 빈 리스트 생성
empty_list = []
# 숫자 리스트 생성
numbers = [1, 2, 3, 4, 5]
# 문자열 리스트 생성
fruits = ["사과", "바나나", "오렌지"]
# 다양한 타입이 섞인 리스트
mixed = [1, "안녕", 3.14, True]
# 중첩 리스트 (리스트 안에 리스트)
nested = [[1, 2], [3, 4], [5, 6]]
2. list() 함수를 사용한 리스트 생성
list() 함수를 사용하여 다른 데이터 타입을 리스트로 변환할 수 있습니다:
<!-- list() 함수 사용 -->
# 문자열을 리스트로 변환 (각 문자가 요소가 됨)
char_list = list("Python")
print(char_list) # ['P', 'y', 't', 'h', 'o', 'n']
# 튜플을 리스트로 변환
tuple_to_list = list((1, 2, 3))
print(tuple_to_list) # [1, 2, 3]
# range를 리스트로 변환
range_to_list = list(range(1, 6))
print(range_to_list) # [1, 2, 3, 4, 5]
파이썬 리스트는 다른 프로그래밍 언어의 배열과 비슷하지만, 훨씬 더 유연합니다. 서로 다른 타입의 데이터를 하나의 리스트에 저장할 수 있고, 크기도 동적으로 조절됩니다.
리스트 요소 접근하기
리스트의 각 요소는 인덱스(index)를 통해 접근할 수 있습니다.
파이썬에서 인덱스는 0부터 시작합니다.
인덱싱(Indexing)
<!-- 리스트 인덱싱 -->
fruits = ["사과", "바나나", "오렌지", "포도", "딸기"]
# 첫 번째 요소 접근 (인덱스 0)
print(fruits[0]) # 사과
# 두 번째 요소 접근 (인덱스 1)
print(fruits[1]) # 바나나
# 마지막 요소 접근 (인덱스 -1)
print(fruits[-1]) # 딸기
# 뒤에서 두 번째 요소 접근 (인덱스 -2)
print(fruits[-2]) # 포도
슬라이싱(Slicing)
슬라이싱을 사용하면 리스트의 일부분을 추출할 수 있습니다:
<!-- 리스트 슬라이싱 -->
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 인덱스 2부터 5까지 (6 미만)
print(numbers[2:6]) # [2, 3, 4, 5]
# 처음부터 인덱스 4까지
print(numbers[:5]) # [0, 1, 2, 3, 4]
# 인덱스 6부터 끝까지
print(numbers[6:]) # [6, 7, 8, 9]
# 처음부터 끝까지 2칸 간격으로
print(numbers[::2]) # [0, 2, 4, 6, 8]
# 리스트 복사하기
copy_numbers = numbers[:]
print(copy_numbers) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 리스트 뒤집기
reversed_numbers = numbers[::-1]
print(reversed_numbers) # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
리스트 수정하기
파이썬 리스트는 가변(mutable) 객체이므로, 생성 후에도 내용을 변경할 수 있습니다.
요소 수정하기
<!-- 요소 수정 -->
fruits = ["사과", "바나나", "오렌지"]
# 인덱스를 사용해 요소 변경
fruits[0] = "키위"
print(fruits) # ['키위', '바나나', '오렌지']
요소 추가하기
<!-- 요소 추가 -->
fruits = ["사과", "바나나", "오렌지"]
# 리스트 끝에 요소 추가
fruits.append("포도")
print(fruits) # ['사과', '바나나', '오렌지', '포도']
# 특정 위치에 요소 삽입
fruits.insert(1, "키위")
print(fruits) # ['사과', '키위', '바나나', '오렌지', '포도']
# 다른 리스트 요소들을 현재 리스트에 추가
more_fruits = ["망고", "파인애플"]
fruits.extend(more_fruits)
print(fruits) # ['사과', '키위', '바나나', '오렌지', '포도', '망고', '파인애플']
# + 연산자로 리스트 연결하기
new_list = fruits + ["수박", "멜론"]
print(new_list) # ['사과', '키위', '바나나', '오렌지', '포도', '망고', '파인애플', '수박', '멜론']
요소 제거하기
<!-- 요소 제거 -->
fruits = ["사과", "키위", "바나나", "오렌지", "포도", "바나나"]
# 값으로 요소 제거 (첫 번째 일치하는 항목만)
fruits.remove("바나나")
print(fruits) # ['사과', '키위', '오렌지', '포도', '바나나']
# 인덱스로 요소 제거하고 반환
removed_fruit = fruits.pop(1) # '키위' 제거
print(removed_fruit) # 키위
print(fruits) # ['사과', '오렌지', '포도', '바나나']
# 인덱스 지정 없이 pop()은 마지막 요소 제거
last_fruit = fruits.pop()
print(last_fruit) # 바나나
print(fruits) # ['사과', '오렌지', '포도']
# 모든 요소 삭제
fruits.clear()
print(fruits) # []
# del 키워드로 요소 또는 리스트 자체 삭제
fruits = ["사과", "바나나", "오렌지"]
del fruits[1] # '바나나' 삭제
print(fruits) # ['사과', '오렌지']
리스트에 없는 요소를 remove()로 삭제하려고 하면 ValueError가 발생합니다. 또한, 범위를 벗어난 인덱스에 접근하면 IndexError가 발생합니다. 이런 오류를 방지하려면 삭제 전에 해당 요소가 리스트에 있는지 확인하는 것이 좋습니다.
주요 리스트 메서드
파이썬 리스트는 다양한 내장 메서드를 제공합니다.
이러한 메서드를 활용하면 리스트를 효율적으로 다룰 수 있습니다.
리스트 정렬하기
<!-- 리스트 정렬 -->
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]
# 원본을 유지하면서 정렬된 새 리스트 반환
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
sorted_numbers = sorted(numbers)
print(sorted_numbers) # [1, 1, 2, 3, 4, 5, 6, 9]
print(numbers) # [3, 1, 4, 1, 5, 9, 2, 6] (원본 유지)
# 문자열 리스트 정렬 (기본: 알파벳 순)
fruits = ["바나나", "사과", "오렌지", "키위"]
fruits.sort()
print(fruits) # ['바나나', '사과', '오렌지', '키위']
# 길이 기준으로 정렬
fruits.sort(key=len)
print(fruits) # ['사과', '키위', '바나나', '오렌지']
리스트 검색과 계산
<!-- 리스트 검색과 계산 -->
numbers = [1, 2, 3, 2, 4, 2, 5]
# 특정 값의 개수 세기
count_2 = numbers.count(2)
print(count_2) # 3
# 특정 값의 첫 번째 인덱스 찾기
index_4 = numbers.index(4)
print(index_4) # 4
# 리스트 요소의 합계
total = sum(numbers)
print(total) # 19
# 최대값과 최소값
maximum = max(numbers)
minimum = min(numbers)
print(maximum, minimum) # 5 1
# 리스트 길이 (요소 개수)
length = len(numbers)
print(length) # 7
리스트 변형하기
<!-- 리스트 변형 -->
numbers = [1, 2, 3, 4, 5]
# 리스트 순서 뒤집기 (원본 변경)
numbers.reverse()
print(numbers) # [5, 4, 3, 2, 1]
# 리스트 복사하기
original = [1, 2, 3]
copy1 = original.copy() # 방법 1
copy2 = list(original) # 방법 2
copy3 = original[:] # 방법 3
original[0] = 99
print(original) # [99, 2, 3]
print(copy1) # [1, 2, 3] (원본 변경에 영향 받지 않음)
리스트 반복문 활용하기
리스트의 모든 요소를 처리할 때는 반복문을 사용하는 것이 일반적입니다.
for 반복문으로 리스트 순회하기
<!-- for 반복문으로 리스트 순회 -->
fruits = ["사과", "바나나", "오렌지", "포도"]
# 기본 for 반복문
for fruit in fruits:
print(fruit)
# 출력:
# 사과
# 바나나
# 오렌지
# 포도
# 인덱스와 함께 순회 (enumerate 사용)
for index, fruit in enumerate(fruits):
print(f"{index}번째: {fruit}")
# 출력:
# 0번째: 사과
# 1번째: 바나나
# 2번째: 오렌지
# 3번째: 포도
# 여러 리스트 동시 순회 (zip 사용)
prices = [1000, 500, 800, 2000]
for fruit, price in zip(fruits, prices):
print(f"{fruit}의 가격: {price}원")
# 출력:
# 사과의 가격: 1000원
# 바나나의 가격: 500원
# 오렌지의 가격: 800원
# 포도의 가격: 2000원
리스트 요소 필터링하기
<!-- 리스트 요소 필터링 -->
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 짝수만 선택하기
even_numbers = []
for num in numbers:
if num % 2 == 0:
even_numbers.append(num)
print(even_numbers) # [2, 4, 6, 8, 10]
# filter 함수 사용하기
def is_odd(n):
return n % 2 == 1
odd_numbers = list(filter(is_odd, numbers))
print(odd_numbers) # [1, 3, 5, 7, 9]
# 람다 함수와 filter 사용
greater_than_5 = list(filter(lambda x: x > 5, numbers))
print(greater_than_5) # [6, 7, 8, 9, 10]
리스트 컴프리헨션
리스트 컴프리헨션(List Comprehension)은
파이썬에서 리스트를 생성하는 간결하고 강력한 방법입니다.
기본 리스트 컴프리헨션
<!-- 기본 리스트 컴프리헨션 -->
# 1부터 10까지의 제곱 리스트 만들기
squares = [x**2 for x in range(1, 11)]
print(squares) # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# 기존 방식과 비교
squares_traditional = []
for x in range(1, 11):
squares_traditional.append(x**2)
print(squares_traditional) # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
조건부 리스트 컴프리헨션
<!-- 조건부 리스트 컴프리헨션 -->
# 1부터 20까지의 짝수만 선택
even_numbers = [x for x in range(1, 21) if x % 2 == 0]
print(even_numbers) # [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
# 조건에 따라 다른 값 할당 (삼항 연산자 활용)
numbers = [1, 2, 3, 4, 5]
result = ["짝수" if x % 2 == 0 else "홀수" for x in numbers]
print(result) # ['홀수', '짝수', '홀수', '짝수', '홀수']
중첩 리스트 컴프리헨션
<!-- 중첩 리스트 컴프리헨션 -->
# 2차원 행렬 생성 (3x4)
matrix = [[i * j for j in range(1, 5)] for i in range(1, 4)]
print(matrix)
# [[1, 2, 3, 4], [2, 4, 6, 8], [3, 6, 9, 12]]
# 중첩 리스트 평탄화(flatten)
nested_list = [[1, 2], [3, 4], [5, 6]]
flattened = [num for sublist in nested_list for num in sublist]
print(flattened) # [1, 2, 3, 4, 5, 6]
리스트 컴프리헨션은 일반 for 루프보다 더 빠르고 메모리 효율적입니다. 하지만 너무 복잡한 컴프리헨션은 가독성을 해칠 수 있으니 적절히 사용하세요. 세 줄 이상의 로직이 필요하다면 일반 for 루프가 더 명확할 수 있습니다.
고급 리스트 활용 기법
이제 리스트의 더 고급 활용 방법을 알아보겠습니다.
map 함수로 리스트 변환하기
<!-- map 함수 사용 -->
# 문자열 리스트를 정수 리스트로 변환
str_numbers = ["1", "2", "3", "4", "5"]
int_numbers = list(map(int, str_numbers))
print(int_numbers) # [1, 2, 3, 4, 5]
# 모든 요소에 함수 적용하기
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, numbers))
print(squared) # [1, 4, 9, 16, 25]
# 여러 리스트의 요소를 조합하기
list1 = [1, 2, 3]
list2 = [10, 20, 30]
combined = list(map(lambda x, y: x + y, list1, list2))
print(combined) # [11, 22, 33]
리스트의 얕은 복사와 깊은 복사
<!-- 얕은 복사와 깊은 복사 -->
import copy
# 원본 리스트 (중첩 리스트 포함)
original = [[1, 2, 3], [4, 5, 6]]
# 얕은 복사 (첫 번째 레벨만 복사)
shallow_copy = original.copy()
shallow_copy[0][0] = 99
print(original) # [[99, 2, 3], [4, 5, 6]] - 원본도 변경됨
print(shallow_copy) # [[99, 2, 3], [4, 5, 6]]
# 깊은 복사 (모든 레벨 복사)
original = [[1, 2, 3], [4, 5, 6]]
deep_copy = copy.deepcopy(original)
deep_copy[0][0] = 99
print(original) # [[1, 2, 3], [4, 5, 6]] - 원본 유지
print(deep_copy) # [[99, 2, 3], [4, 5, 6]]
리스트를 활용한 스택과 큐 구현
<!-- 스택과 큐 구현 -->
# 스택 구현 (Last In, First Out)
stack = []
stack.append(1) # 삽입
stack.append(2)
stack.append(3)
print(stack) # [1, 2, 3]
popped = stack.pop() # 추출
print(popped) # 3
print(stack) # [1, 2]
# 큐 구현 (First In, First Out)
from collections import deque
queue = deque([])
queue.append(1) # 삽입
queue.append(2)
queue.append(3)
print(queue) # deque([1, 2, 3])
first = queue.popleft() # 추출
print(first) # 1
print(queue) # deque([2, 3])
리스트 성능 최적화 팁
<!-- 리스트 성능 최적화 -->
# 1. append()는 O(1), insert(0, item)은 O(n) - 가능하면 append 사용
# 2. 큰 리스트 다룰 때는 collections.deque 사용 고려
# 3. in 연산자는 O(n) - 빠른 검색이 필요하면 set이나 dictionary 사용
# 4. 리스트 컴프리헨션은 일반 for 루프보다 효율적
# 예: 리스트 대신 집합(set) 사용하여 검색 속도 향상
numbers_list = list(range(10000))
numbers_set = set(range(10000))
# 리스트 검색 (느림)
import time
start = time.time()
9999 in numbers_list
end = time.time()
print(f"리스트 검색 시간: {end - start:.10f}초")
# 집합 검색 (빠름)
start = time.time()
9999 in numbers_set
end = time.time()
print(f"집합 검색 시간: {end - start:.10f}초")
정리 및 결론
지금까지 파이썬 리스트의 기본부터 고급 활용법까지 알아보았습니다.
리스트는 파이썬 프로그래밍의 핵심 자료구조로,
다양한 데이터를 효과적으로 관리하고 처리하는 데 필수적입니다.
핵심 요약
- 리스트 생성: 대괄호 [] 또는 list() 함수 사용
- 요소 접근: 인덱싱과 슬라이싱으로 요소에 접근
- 리스트 수정: 요소 추가(append, insert, extend), 삭제(remove, pop, clear), 변경 가능
- 주요 메서드: sort(), count(), index(), reverse(), copy() 등
- 반복 처리: for 루프, enumerate(), zip() 활용
- 리스트 컴프리헨션: 간결하고 효율적인 리스트 생성 방법
- 고급 기능: map(), filter(), 깊은 복사, 얕은 복사, 스택/큐 구현
- 데이터 분석과 처리에서는 리스트 컴프리헨션과 map/filter 함수를 활용하면 코드가 간결해집니다.
- 대용량 데이터를 다룰 때는 리스트 대신 NumPy 배열이나 Pandas DataFrame을 고려하세요.
- 중첩 리스트를 다룰 때는 깊은 복사(deepcopy)를 사용해 의도치 않은 데이터 변경을 방지하세요.
- 리스트 메서드의 시간 복잡도를 이해하고 상황에 맞게 최적화된 방법을 선택하세요.
파이썬 리스트는 단순한 데이터 저장소를 넘어 강력한 프로그래밍 도구입니다.
[ 파이썬 관련 블로그 글 목록 ]
파이썬(Python) 블로그 목록