본문으로 건너뛰기

리스트와 튜플

리스트 (List)

리스트는 순서가 있는 변경 가능한 컬렉션입니다.

리스트 생성

# 빈 리스트
empty = []
empty = list()

# 값이 있는 리스트
numbers = [1, 2, 3, 4, 5]
names = ["홍길동", "김철수", "이영희"]
mixed = [1, "hello", 3.14, True]

# 중첩 리스트
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]

# range로 생성
numbers = list(range(1, 6)) # [1, 2, 3, 4, 5]
evens = list(range(0, 11, 2)) # [0, 2, 4, 6, 8, 10]

인덱싱과 슬라이싱

fruits = ["사과", "바나나", "체리", "포도", "멜론"]

# 인덱싱
print(fruits[0]) # 사과
print(fruits[-1]) # 멜론
print(fruits[2]) # 체리

# 슬라이싱
print(fruits[1:4]) # ['바나나', '체리', '포도']
print(fruits[:3]) # ['사과', '바나나', '체리']
print(fruits[2:]) # ['체리', '포도', '멜론']
print(fruits[::2]) # ['사과', '체리', '멜론'] (2칸씩)
print(fruits[::-1]) # ['멜론', '포도', '체리', '바나나', '사과'] (역순)

# 2차원 리스트
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(matrix[0]) # [1, 2, 3]
print(matrix[1][2]) # 6

리스트 수정

fruits = ["사과", "바나나", "체리"]

# 요소 변경
fruits[1] = "딸기"
print(fruits) # ['사과', '딸기', '체리']

# 슬라이스 변경
numbers = [1, 2, 3, 4, 5]
numbers[1:4] = [20, 30]
print(numbers) # [1, 20, 30, 5]

# 요소 추가
fruits.append("포도") # 끝에 추가
print(fruits) # ['사과', '딸기', '체리', '포도']

fruits.insert(1, "망고") # 특정 위치에 추가
print(fruits) # ['사과', '망고', '딸기', '체리', '포도']

# 리스트 확장
more_fruits = ["멜론", "수박"]
fruits.extend(more_fruits) # 여러 요소 추가
print(fruits)

# 요소 삭제
fruits.remove("망고") # 값으로 삭제
print(fruits)

popped = fruits.pop() # 마지막 요소 제거하고 반환
print(popped) # 수박

fruits.pop(0) # 특정 위치 제거
print(fruits)

del fruits[0] # 인덱스로 삭제
print(fruits)

fruits.clear() # 전체 삭제
print(fruits) # []

리스트 연산

# 연결
list1 = [1, 2, 3]
list2 = [4, 5, 6]
combined = list1 + list2
print(combined) # [1, 2, 3, 4, 5, 6]

# 반복
repeated = [0] * 5
print(repeated) # [0, 0, 0, 0, 0]

# 길이
print(len([1, 2, 3, 4, 5])) # 5

# 포함 여부
fruits = ["사과", "바나나", "체리"]
print("사과" in fruits) # True
print("포도" in fruits) # False
print("포도" not in fruits) # True

# 개수 세기
numbers = [1, 2, 2, 3, 2, 4]
print(numbers.count(2)) # 3

# 인덱스 찾기
fruits = ["사과", "바나나", "체리", "바나나"]
print(fruits.index("바나나")) # 1 (첫 번째 위치)

리스트 정렬

numbers = [3, 1, 4, 1, 5, 9, 2]

# sort() - 원본 변경
numbers.sort()
print(numbers) # [1, 1, 2, 3, 4, 5, 9]

numbers.sort(reverse=True)
print(numbers) # [9, 5, 4, 3, 2, 1, 1]

# sorted() - 새 리스트 반환
numbers = [3, 1, 4, 1, 5, 9, 2]
sorted_numbers = sorted(numbers)
print(sorted_numbers) # [1, 1, 2, 3, 4, 5, 9]
print(numbers) # [3, 1, 4, 1, 5, 9, 2] (원본 유지)

# 문자열 정렬
words = ["banana", "apple", "cherry"]
words.sort()
print(words) # ['apple', 'banana', 'cherry']

# 역순
numbers.reverse()
print(numbers)

# 키 함수로 정렬
words = ["apple", "pie", "zoo", "a"]
words.sort(key=len) # 길이순
print(words) # ['a', 'pie', 'zoo', 'apple']

# 복잡한 정렬
students = [
{"name": "홍길동", "score": 85},
{"name": "김철수", "score": 92},
{"name": "이영희", "score": 78}
]
students.sort(key=lambda x: x["score"], reverse=True)
print(students)

리스트 컴프리헨션

# 기본 형태
squares = [x ** 2 for x in range(1, 6)]
print(squares) # [1, 4, 9, 16, 25]

# 조건 포함
evens = [x for x in range(1, 11) if x % 2 == 0]
print(evens) # [2, 4, 6, 8, 10]

# if-else
numbers = [1, 2, 3, 4, 5]
result = ["짝수" if x % 2 == 0 else "홀수" for x in numbers]
print(result) # ['홀수', '짝수', '홀수', '짝수', '홀수']

# 중첩 리스트
matrix = [[i*j for j in range(1, 4)] for i in range(1, 4)]
print(matrix) # [[1, 2, 3], [2, 4, 6], [3, 6, 9]]

# 평탄화
nested = [[1, 2], [3, 4], [5, 6]]
flat = [num for sublist in nested for num in sublist]
print(flat) # [1, 2, 3, 4, 5, 6]

# 실용 예제
names = ["홍길동", "김철수", "이영희"]
upper_names = [name.upper() for name in names]
print(upper_names)

# 조건부 변환
scores = [85, 92, 78, 95, 88]
grades = ["Pass" if score >= 80 else "Fail" for score in scores]
print(grades)

튜플 (Tuple)

튜플은 순서가 있는 변경 불가능한 컬렉션입니다.

튜플 생성

# 빈 튜플
empty = ()
empty = tuple()

# 값이 있는 튜플
numbers = (1, 2, 3, 4, 5)
mixed = (1, "hello", 3.14, True)

# 괄호 없이도 가능
point = 10, 20
print(point) # (10, 20)

# 요소 1개인 튜플 (쉼표 필수!)
single = (5,)
not_tuple = (5) # 이건 그냥 int
print(type(single)) # <class 'tuple'>
print(type(not_tuple)) # <class 'int'>

# 리스트에서 변환
numbers_list = [1, 2, 3]
numbers_tuple = tuple(numbers_list)
print(numbers_tuple) # (1, 2, 3)

튜플 접근

colors = ("빨강", "초록", "파랑", "노랑")

# 인덱싱
print(colors[0]) # 빨강
print(colors[-1]) # 노랑

# 슬라이싱
print(colors[1:3]) # ('초록', '파랑')

# 길이
print(len(colors)) # 4

# 포함 여부
print("빨강" in colors) # True

# 개수와 인덱스
numbers = (1, 2, 2, 3, 2)
print(numbers.count(2)) # 3
print(numbers.index(3)) # 3

튜플 연산

# 연결
tuple1 = (1, 2, 3)
tuple2 = (4, 5, 6)
combined = tuple1 + tuple2
print(combined) # (1, 2, 3, 4, 5, 6)

# 반복
repeated = (0,) * 5
print(repeated) # (0, 0, 0, 0, 0)

# 비교
print((1, 2) < (1, 3)) # True (사전순)
print((1, 2) == (1, 2)) # True

튜플 언패킹

# 기본 언패킹
point = (10, 20)
x, y = point
print(x, y) # 10 20

# 여러 값 반환
def get_user():
return "홍길동", 25, "서울"

name, age, city = get_user()
print(name, age, city)

# 확장 언패킹 (Python 3+)
numbers = (1, 2, 3, 4, 5)
first, *middle, last = numbers
print(first) # 1
print(middle) # [2, 3, 4]
print(last) # 5

# 값 교환
a, b = 1, 2
a, b = b, a
print(a, b) # 2 1

# 무시할 값
point = (10, 20, 30)
x, _, z = point
print(x, z) # 10 30

튜플 vs 리스트

# 리스트: 변경 가능
fruits_list = ["사과", "바나나"]
fruits_list.append("체리") # ✅
print(fruits_list)

# 튜플: 변경 불가능
fruits_tuple = ("사과", "바나나")
# fruits_tuple.append("체리") # ❌ AttributeError

# 튜플은 더 빠르고 메모리 효율적
import sys
list_data = [1, 2, 3, 4, 5]
tuple_data = (1, 2, 3, 4, 5)
print(sys.getsizeof(list_data)) # 104
print(sys.getsizeof(tuple_data)) # 88

# 튜플은 딕셔너리 키로 사용 가능
locations = {
(0, 0): "원점",
(1, 0): "동쪽",
(0, 1): "북쪽"
}
print(locations[(0, 0)]) # 원점

# 리스트는 불가능
# locations = {[0, 0]: "원점"} # ❌ TypeError

실전 예제

학생 성적 관리

# 학생 정보 (튜플로 불변 데이터)
students = [
("홍길동", 85, 90, 88),
("김철수", 92, 88, 95),
("이영희", 78, 85, 82)
]

# 평균 계산
def calculate_average(scores):
name, *grades = scores
avg = sum(grades) / len(grades)
return name, avg

for student in students:
name, avg = calculate_average(student)
print(f"{name}: 평균 {avg:.1f}점")

# 최고 점수 학생
best_student = max(students, key=lambda x: sum(x[1:]))
print(f"최고 점수: {best_student[0]}")

To-Do 리스트

class TodoList:
def __init__(self):
self.tasks = []

def add(self, task):
"""할 일 추가"""
self.tasks.append({"task": task, "done": False})
print(f"'{task}' 추가됨")

def complete(self, index):
"""할 일 완료"""
if 0 <= index < len(self.tasks):
self.tasks[index]["done"] = True
print(f"'{self.tasks[index]['task']}' 완료!")

def remove(self, index):
"""할 일 삭제"""
if 0 <= index < len(self.tasks):
task = self.tasks.pop(index)
print(f"'{task['task']}' 삭제됨")

def show(self):
"""할 일 목록 출력"""
if not self.tasks:
print("할 일이 없습니다!")
return

print("\n=== To-Do 리스트 ===")
for i, item in enumerate(self.tasks):
status = "✓" if item["done"] else " "
print(f"{i+1}. [{status}] {item['task']}")

# 사용 예제
todo = TodoList()
todo.add("Python 공부하기")
todo.add("운동하기")
todo.add("책 읽기")
todo.show()
todo.complete(0)
todo.show()

데이터 분석

# 판매 데이터 (제품명, 수량, 가격)
sales = [
("노트북", 5, 1200000),
("마우스", 30, 25000),
("키보드", 15, 85000),
("모니터", 8, 350000)
]

# 총 매출 계산
def calculate_revenue(sales_data):
total = 0
for product, quantity, price in sales_data:
revenue = quantity * price
total += revenue
print(f"{product}: {revenue:,}원")
return total

print("\n=== 제품별 매출 ===")
total_revenue = calculate_revenue(sales)
print(f"\n총 매출: {total_revenue:,}원")

# 최다 판매 제품
best_seller = max(sales, key=lambda x: x[1])
print(f"\n최다 판매: {best_seller[0]} ({best_seller[1]}개)")

# 매출 상위 제품 정렬
sorted_sales = sorted(sales, key=lambda x: x[1] * x[2], reverse=True)
print("\n=== 매출 순위 ===")
for rank, (product, qty, price) in enumerate(sorted_sales, 1):
print(f"{rank}. {product}: {qty * price:,}원")

좌표 관리

# 2D 좌표들
points = [(0, 0), (3, 4), (1, 1), (5, 12)]

# 원점으로부터 거리 계산
def distance_from_origin(point):
x, y = point
return (x**2 + y**2) ** 0.5

# 거리 계산 및 정렬
distances = [(point, distance_from_origin(point)) for point in points]
distances.sort(key=lambda x: x[1])

print("=== 원점으로부터의 거리 ===")
for point, dist in distances:
print(f"{point}: {dist:.2f}")

# 가장 먼 점
farthest = max(points, key=distance_from_origin)
print(f"\n가장 먼 점: {farthest}")

자주 묻는 질문

Q1. 언제 리스트를, 언제 튜플을 사용하나요?

A: 변경 필요 여부로 결정

# 리스트: 변경 가능한 데이터
shopping_cart = ["사과", "바나나"]
shopping_cart.append("우유") # 장바구니는 계속 변함

# 튜플: 변경 불가능한 데이터
birth_date = (1990, 5, 15) # 생년월일은 고정
coordinates = (37.5665, 126.9780) # 좌표는 고정

Q2. 리스트 복사는 어떻게 하나요?

A: 얕은 복사와 깊은 복사 구분

original = [1, 2, 3]

# 참조 복사 (같은 객체)
ref = original
ref.append(4)
print(original) # [1, 2, 3, 4] - 원본도 변경됨!

# 얕은 복사 (새 객체)
shallow = original.copy()
# 또는
shallow = original[:]
# 또는
shallow = list(original)

shallow.append(5)
print(original) # [1, 2, 3, 4] - 원본 유지

# 깊은 복사 (중첩 리스트)
import copy
nested = [[1, 2], [3, 4]]
deep = copy.deepcopy(nested)
deep[0].append(3)
print(nested) # [[1, 2], [3, 4]] - 원본 유지

Q3. 리스트가 비어있는지 확인하는 방법은?

A: 여러 방법이 있습니다

my_list = []

# 방법 1: 길이 확인
if len(my_list) == 0:
print("비어있음")

# 방법 2: 불린 변환 (Pythonic!)
if not my_list:
print("비어있음")

# 방법 3: 비교
if my_list == []:
print("비어있음")

Q4. 다차원 리스트 초기화 주의사항은?

A: 참조 복사를 조심하세요

# ❌ 잘못된 방법
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]] - 정상

다음 단계

리스트와 튜플을 마스터했습니다!

핵심 정리:
✅ 리스트: 변경 가능, 다양한 메서드
✅ 튜플: 변경 불가능, 빠르고 안전
✅ 인덱싱, 슬라이싱, 언패킹
✅ 리스트 컴프리헨션
✅ 실전 활용 예제

다음 단계: 딕셔너리와 셋에서 더 많은 컬렉션을 배워보세요!