Day 7에서 리스트를 배웠죠? 🤔 ──리스트는 “수정 가능한 리스트”라면, 튜플은 “수정 불가능한 리스트”입니다! 😊
좌표 (x, y), RGB 색상 (255, 0, 0), 날짜 (2025, 3, 11)… 변하면 안 되는 값들을 저장할 때 튜플을 사용합니다.
“한 번 정한 값은 절대 바꾸지 못한다”는 특성이 때로는 버그를 방지하는 최고의 안전장치가 됩니다! 💡
(20분 완독 ⭐⭐)
🎯 오늘의 학습 목표
📚 사전 지식
🎯 학습 목표 1: 튜플이 무엇인지 이해하기
한 줄 설명
튜플 = 수정할 수 없는(immutable) 순서가 있는 데이터 모음
리스트처럼 여러 값을 저장하지만, 한 번 만들면 절대 변경할 수 없습니다.
실생활 비유
1
2
3
4
5
6
7
| 리스트: 쇼핑 목록 (추가, 삭제, 수정 가능)
["사과", "바나나", "우유"]
→ "우유" 대신 "요구르트"로 변경 가능!
튜플: 생년월일 (절대 변경 불가)
(1990, 5, 15)
→ 한 번 태어난 날짜는 바꿀 수 없음!
|
왜 튜플을 사용할까?
1. 데이터 보호
1
2
3
4
5
6
7
8
| # 리스트는 실수로 변경될 수 있음
colors = ["빨강", "파랑", "초록"]
colors[0] = "검정" # 변경됨!
print(colors) # ['검정', '파랑', '초록']
# 튜플은 변경 불가능 → 안전!
colors = ("빨강", "파랑", "초록")
# colors[0] = "검정" # TypeError 발생!
|
2. 성능 향상
튜플은 리스트보다 메모리를 적게 사용하고 더 빠릅니다.
1
2
3
4
5
6
7
| import sys
list_data = [1, 2, 3, 4, 5]
tuple_data = (1, 2, 3, 4, 5)
print(f"리스트 크기: {sys.getsizeof(list_data)} bytes")
print(f"튜플 크기: {sys.getsizeof(tuple_data)} bytes")
|
출력:
1
2
| 리스트 크기: 104 bytes
튜플 크기: 80 bytes
|
3. 딕셔너리 키로 사용 가능
1
2
3
4
5
6
| # 리스트는 딕셔너리 키로 사용 불가
# location = {[10, 20]: "서울"} # TypeError!
# 튜플은 딕셔너리 키로 사용 가능
location = {(10, 20): "서울", (30, 40): "부산"}
print(location[(10, 20)]) # 서울
|
🎯 학습 목표 2: 튜플과 리스트의 차이 비교하기
리스트 vs 튜플 비교표
| 특성 | 리스트 [] | 튜플 () |
| 수정 가능 | ✅ 가능 | ❌ 불가능 |
| 추가/삭제 | ✅ 가능 | ❌ 불가능 |
| 속도 | 보통 | 빠름 |
| 메모리 | 많이 사용 | 적게 사용 |
| dict 키 | ❌ 불가능 | ✅ 가능 |
| 사용 시기 | 변경 필요한 데이터 | 고정된 데이터 |
실전 비교 예제
1
2
3
4
5
6
7
8
9
10
| # 리스트: 할 일 목록 (추가/삭제 자주 발생)
todo_list = ["공부하기", "운동하기", "독서하기"]
todo_list.append("청소하기") # 추가 가능
todo_list.remove("독서하기") # 삭제 가능
print(todo_list) # ['공부하기', '운동하기', '청소하기']
# 튜플: 좌표 (한 번 정해지면 변경 안 됨)
position = (10, 20)
# position[0] = 30 # TypeError! 변경 불가
print(f"위치: x={position[0]}, y={position[1]}")
|
리스트에서만 가능한 것들
1
2
3
4
5
6
7
8
9
10
11
12
13
| # 리스트 전용 메서드들
my_list = [1, 2, 3]
my_list.append(4) # 추가
my_list.remove(2) # 삭제
my_list[0] = 10 # 수정
my_list.sort() # 정렬
my_list.reverse() # 뒤집기
# 튜플에서는 모두 불가능!
my_tuple = (1, 2, 3)
# my_tuple.append(4) # AttributeError!
# my_tuple.remove(2) # AttributeError!
# my_tuple[0] = 10 # TypeError!
|
튜플과 리스트 공통 기능
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # 둘 다 가능한 것들
my_list = [1, 2, 3, 4, 5]
my_tuple = (1, 2, 3, 4, 5)
# 인덱싱
print(my_list[0]) # 1
print(my_tuple[0]) # 1
# 슬라이싱
print(my_list[1:3]) # [2, 3]
print(my_tuple[1:3]) # (2, 3)
# len(), in, count(), index()
print(len(my_list)) # 5
print(3 in my_tuple) # True
print(my_list.count(3)) # 1
print(my_tuple.index(4)) # 3
|
🎯 학습 목표 3: 튜플 생성과 접근 방법
튜플 생성하기
방법 1: 괄호 사용
1
2
3
4
| # 기본 형태
fruits = ("사과", "바나나", "포도")
print(fruits) # ('사과', '바나나', '포도')
print(type(fruits)) # <class 'tuple'>
|
방법 2: 괄호 없이 (쉼표만으로)
1
2
3
4
| # 쉼표만으로도 튜플 생성 가능!
numbers = 1, 2, 3, 4, 5
print(numbers) # (1, 2, 3, 4, 5)
print(type(numbers)) # <class 'tuple'>
|
방법 3: tuple() 함수 사용
1
2
3
4
5
6
7
8
9
| # 리스트를 튜플로 변환
my_list = [1, 2, 3, 4, 5]
my_tuple = tuple(my_list)
print(my_tuple) # (1, 2, 3, 4, 5)
# 문자열을 튜플로 변환
text = "Python"
char_tuple = tuple(text)
print(char_tuple) # ('P', 'y', 't', 'h', 'o', 'n')
|
방법 4: 빈 튜플과 단일 요소 튜플
1
2
3
4
5
6
7
8
9
10
11
12
13
| # 빈 튜플
empty_tuple = ()
print(empty_tuple) # ()
print(len(empty_tuple)) # 0
# 단일 요소 튜플 - 쉼표 필수!
single_tuple = (5,) # 쉼표 있어야 함!
print(single_tuple) # (5,)
print(type(single_tuple)) # <class 'tuple'>
# 쉼표 없으면 그냥 숫자
not_tuple = (5)
print(type(not_tuple)) # <class 'int'>
|
💡 중요: 단일 요소 튜플은 반드시 쉼표(,)를 붙여야 합니다!
튜플 요소 접근하기
인덱싱
1
2
3
4
5
6
7
8
9
| colors = ("빨강", "파랑", "초록", "노랑", "보라")
# 양수 인덱스
print(colors[0]) # 빨강
print(colors[2]) # 초록
# 음수 인덱스
print(colors[-1]) # 보라
print(colors[-2]) # 노랑
|
슬라이싱
1
2
3
4
5
6
7
| numbers = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
print(numbers[2:5]) # (2, 3, 4)
print(numbers[:3]) # (0, 1, 2)
print(numbers[7:]) # (7, 8, 9)
print(numbers[::2]) # (0, 2, 4, 6, 8) - 짝수만
print(numbers[::-1]) # (9, 8, 7, 6, 5, 4, 3, 2, 1, 0) - 역순
|
튜플 언패킹 (Unpacking)
튜플의 가장 강력한 기능 중 하나!
1
2
3
4
5
6
7
8
9
| # 기본 언패킹
point = (10, 20)
x, y = point
print(f"x: {x}, y: {y}") # x: 10, y: 20
# 여러 값 한 번에
person = ("홍길동", 25, "서울")
name, age, city = person
print(f"{name}님은 {age}세, {city} 거주")
|
함수 반환값으로 활용
1
2
3
4
5
6
7
| def get_min_max(numbers):
"""최소값과 최대값을 튜플로 반환"""
return (min(numbers), max(numbers))
numbers = [5, 2, 9, 1, 7]
min_val, max_val = get_min_max(numbers)
print(f"최소값: {min_val}, 최대값: {max_val}")
|
출력:
* 연산자로 나머지 받기
1
2
3
4
5
6
7
8
9
10
| # 첫 번째와 나머지
numbers = (1, 2, 3, 4, 5)
first, *rest = numbers
print(f"첫 번째: {first}") # 1
print(f"나머지: {rest}") # [2, 3, 4, 5]
# 첫 번째, 마지막, 중간
first, *middle, last = numbers
print(f"첫: {first}, 중간: {middle}, 끝: {last}")
# 첫: 1, 중간: [2, 3, 4], 끝: 5
|
튜플 메서드
튜플은 수정 불가능하므로 메서드가 2개뿐입니다!
1
2
3
4
5
6
7
8
| numbers = (1, 2, 3, 2, 4, 2, 5)
# count() - 특정 값의 개수
print(numbers.count(2)) # 3
# index() - 특정 값의 첫 번째 인덱스
print(numbers.index(4)) # 4
print(numbers.index(2)) # 1 (첫 번째 2의 위치)
|
🎯 학습 목표 4: 튜플의 실전 활용법
활용 1: 여러 값 동시에 할당
1
2
3
4
5
6
7
| # 변수 교환 (swap)
a = 10
b = 20
# 리스트 없이 간단하게!
a, b = b, a
print(f"a: {a}, b: {b}") # a: 20, b: 10
|
활용 2: 함수에서 여러 값 반환
1
2
3
4
5
6
7
| def calculate(a, b):
"""사칙연산 결과를 튜플로 반환"""
return (a + b, a - b, a * b, a / b)
# 결과를 한 번에 받기
add, sub, mul, div = calculate(10, 5)
print(f"덧셈: {add}, 뺄셈: {sub}, 곱셈: {mul}, 나눗셈: {div}")
|
출력:
1
| 덧셈: 15, 뺄셈: 5, 곱셈: 50, 나눗셈: 2.0
|
활용 3: 좌표와 위치 저장
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # 2D 좌표
player_position = (100, 200)
enemy_position = (150, 180)
# 3D 좌표
cube_position = (10, 20, 30)
# RGB 색상
red = (255, 0, 0)
blue = (0, 0, 255)
white = (255, 255, 255)
print(f"플레이어 위치: x={player_position[0]}, y={player_position[1]}")
print(f"빨간색 RGB: {red}")
|
활용 4: 상수 그룹 정의
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # 요일 튜플 (변경하면 안 되는 데이터)
WEEKDAYS = ("월", "화", "수", "목", "금", "토", "일")
# 방향 벡터
DIRECTIONS = {
"up": (0, -1),
"down": (0, 1),
"left": (-1, 0),
"right": (1, 0)
}
# 사용
today_index = 2
print(f"오늘은 {WEEKDAYS[today_index]}요일") # 오늘은 수요일
dx, dy = DIRECTIONS["right"]
print(f"오른쪽 이동: ({dx}, {dy})")
|
활용 5: 딕셔너리 키로 사용
1
2
3
4
5
6
7
8
9
10
11
12
| # 좌표를 키로 사용하는 지도 데이터
game_map = {
(0, 0): "시작점",
(1, 0): "나무",
(2, 0): "적",
(3, 0): "보물",
(4, 0): "출구"
}
# 특정 위치 확인
position = (2, 0)
print(f"{position}에는 {game_map[position]}이(가) 있습니다.")
|
활용 6: 루프에서 여러 값 처리
1
2
3
4
5
6
7
8
9
10
| # 학생 정보 (이름, 나이, 점수)
students = [
("홍길동", 20, 85),
("김철수", 22, 90),
("이영희", 21, 95)
]
# 언패킹하며 반복
for name, age, score in students:
print(f"{name} ({age}세): {score}점")
|
출력:
1
2
3
| 홍길동 (20세): 85점
김철수 (22세): 90점
이영희 (21세): 95점
|
활용 7: enumerate()와 함께 사용
1
2
3
4
5
| fruits = ["사과", "바나나", "포도"]
# enumerate()는 (인덱스, 값) 튜플을 반환
for index, fruit in enumerate(fruits):
print(f"{index}: {fruit}")
|
출력:
💻 실전 예제
예제 1: 분수 계산기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| def add_fractions(frac1, frac2):
"""분수 덧셈 (튜플로 분수 표현)"""
# frac1 = (분자, 분모), frac2 = (분자, 분모)
n1, d1 = frac1
n2, d2 = frac2
# 통분하여 계산
result_n = n1 * d2 + n2 * d1
result_d = d1 * d2
return (result_n, result_d)
# 1/2 + 1/3 계산
result = add_fractions((1, 2), (1, 3))
print(f"결과: {result[0]}/{result[1]}") # 결과: 5/6
|
예제 2: 거리 계산
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| import math
def distance(point1, point2):
"""두 점 사이의 거리 계산"""
x1, y1 = point1
x2, y2 = point2
return math.sqrt((x2 - x1)**2 + (y2 - y1)**2)
# 두 점 사이의 거리
p1 = (0, 0)
p2 = (3, 4)
dist = distance(p1, p2)
print(f"{p1}와 {p2} 사이의 거리: {dist}") # 5.0
|
예제 3: 성적 처리 시스템
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| def process_scores(student_data):
"""학생 성적 처리 (튜플 활용)"""
results = []
for name, *scores in student_data:
total = sum(scores)
average = total / len(scores)
results.append((name, total, average))
return results
# 학생별 과목 점수
students = [
("홍길동", 85, 90, 88),
("김철수", 92, 88, 95),
("이영희", 78, 85, 80)
]
results = process_scores(students)
print("=== 성적 결과 ===")
for name, total, avg in results:
print(f"{name}: 총점 {total}, 평균 {avg:.1f}")
|
출력:
1
2
3
4
| === 성적 결과 ===
홍길동: 총점 263, 평균 87.7
김철수: 총점 275, 평균 91.7
이영희: 총점 243, 평균 81.0
|
💡 실전 팁 & 주의사항
Tip 1: 단일 요소 튜플은 쉼표 필수
1
2
3
4
5
6
7
| # ❌ 잘못된 방법
not_tuple = (5)
print(type(not_tuple)) # <class 'int'>
# ✅ 올바른 방법
is_tuple = (5,) # 쉼표 필수!
print(type(is_tuple)) # <class 'tuple'>
|
Tip 2: 튜플은 변경 불가능하지만 연결은 가능
1
2
3
4
5
6
7
8
9
10
11
| # 기존 튜플은 변경 안 되지만
tuple1 = (1, 2, 3)
tuple2 = (4, 5, 6)
# 연결하여 새로운 튜플 생성은 가능!
combined = tuple1 + tuple2
print(combined) # (1, 2, 3, 4, 5, 6)
# 반복도 가능
repeated = tuple1 * 3
print(repeated) # (1, 2, 3, 1, 2, 3, 1, 2, 3)
|
Tip 3: 튜플 안의 리스트는 수정 가능
1
2
3
4
5
6
7
8
9
| # 튜플 안에 리스트가 있으면
mixed = (1, 2, [3, 4, 5])
# 튜플 자체는 변경 불가
# mixed[0] = 10 # TypeError!
# 하지만 내부 리스트는 변경 가능!
mixed[2].append(6)
print(mixed) # (1, 2, [3, 4, 5, 6])
|
Tip 4: 튜플 vs 리스트 선택 기준
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # ✅ 튜플 사용
# - 데이터가 변경되면 안 될 때
birth_date = (1990, 5, 15)
# - 함수 반환값
def get_user_info():
return ("홍길동", 25, "서울")
# - 딕셔너리 키
locations = {(10, 20): "서울"}
# ✅ 리스트 사용
# - 데이터를 추가/삭제/수정해야 할 때
shopping_list = ["우유", "빵", "계란"]
shopping_list.append("과일")
|
Tip 5: 언패킹 시 개수 일치 필수
1
2
3
4
5
6
7
8
9
10
11
| # ❌ 개수 불일치
point = (10, 20, 30)
# x, y = point # ValueError! 3개를 2개 변수에 못 담음
# ✅ 개수 일치
x, y, z = point
print(f"x: {x}, y: {y}, z: {z}")
# ✅ * 사용하여 나머지 받기
first, *rest = point
print(f"첫 번째: {first}, 나머지: {rest}")
|
Tip 6: 괄호 생략 가능 (하지만 명시하는 게 좋음)
1
2
3
4
5
6
| # 괄호 없어도 튜플
coordinates = 10, 20
print(type(coordinates)) # <class 'tuple'>
# 하지만 가독성을 위해 괄호 사용 추천
coordinates = (10, 20) # 더 명확!
|
🧪 연습 문제
문제 1: 튜플로 RGB 색상 관리
RGB 색상을 튜플로 저장하고, 각 색상의 평균을 계산하세요.
💡 힌트
1
2
3
4
| # 힌트:
# 1. RGB는 (R, G, B) 형태의 튜플
# 2. sum() 함수로 합계 구하기
# 3. len() 또는 3으로 나누기
|
✅ 정답
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| def color_average(rgb):
"""RGB 색상의 평균 계산"""
r, g, b = rgb
average = (r + g + b) / 3
return average
# 테스트
red = (255, 0, 0)
green = (0, 255, 0)
blue = (0, 0, 255)
purple = (128, 0, 128)
print(f"빨강 평균: {color_average(red):.1f}")
print(f"초록 평균: {color_average(green):.1f}")
print(f"파랑 평균: {color_average(blue):.1f}")
print(f"보라 평균: {color_average(purple):.1f}")
|
문제 2: 좌표 리스트에서 원점에 가장 가까운 점 찾기
여러 좌표 중에서 (0, 0)에 가장 가까운 좌표를 찾으세요.
✅ 정답
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| import math
def find_closest_to_origin(points):
"""원점에 가장 가까운 점 찾기"""
closest_point = points[0]
min_distance = math.sqrt(points[0][0]**2 + points[0][1]**2)
for point in points:
x, y = point
distance = math.sqrt(x**2 + y**2)
if distance < min_distance:
min_distance = distance
closest_point = point
return closest_point, min_distance
# 테스트
coordinates = [
(3, 4), # 거리: 5
(1, 1), # 거리: 1.41
(5, 12), # 거리: 13
(2, 2) # 거리: 2.83
]
point, dist = find_closest_to_origin(coordinates)
print(f"원점에 가장 가까운 점: {point}")
print(f"거리: {dist:.2f}")
|
문제 3: 튜플로 학생 순위 매기기
학생들의 (이름, 점수) 튜플 리스트를 점수 기준으로 정렬하세요.
✅ 정답
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| def rank_students(students):
"""점수 기준으로 학생 순위 매기기"""
# 점수(인덱스 1) 기준으로 내림차순 정렬
sorted_students = sorted(students, key=lambda x: x[1], reverse=True)
print("=== 학생 순위 ===")
for rank, (name, score) in enumerate(sorted_students, 1):
print(f"{rank}등: {name} ({score}점)")
# 테스트
students = [
("홍길동", 85),
("김철수", 92),
("이영희", 95),
("박민수", 78),
("최지수", 88)
]
rank_students(students)
|
출력:
1
2
3
4
5
6
| === 학생 순위 ===
1등: 이영희 (95점)
2등: 김철수 (92점)
3등: 최지수 (88점)
4등: 홍길동 (85점)
5등: 박민수 (78점)
|
📝 오늘 배운 내용 정리
- 튜플 정의: 수정 불가능한(immutable) 순서가 있는 데이터 모음
- 리스트와 차이: 튜플은 변경 불가, 리스트는 변경 가능
- 튜플 생성:
(), 쉼표, tuple() 함수 사용 - 튜플 언패킹: 여러 값을 한 번에 변수에 할당
- 활용: 좌표, 색상, 함수 반환값, 딕셔너리 키 등
핵심 포인트:
- 튜플은 한 번 만들면 절대 변경할 수 없습니다
- 단일 요소 튜플은 쉼표 필수:
(5,) - 리스트보다 빠르고 메모리 효율적
- 변경하면 안 되는 데이터에 튜플 사용
💬 자주 묻는 질문
Q. 튜플과 리스트 중 뭘 써야 하나요? → 데이터가 변경되면 안 되면 튜플, 변경이 필요하면 리스트!
Q. 튜플은 왜 수정이 안 되나요? → 데이터 보호와 성능 향상을 위해 설계되었습니다.
Q. 튜플을 리스트로 바꿀 수 있나요? → 네! list(my_tuple)로 변환 가능합니다.
Q. 단일 요소 튜플에 왜 쉼표가 필요한가요? → (5)는 숫자 5이고, (5,)가 튜플입니다. 쉼표로 구분해야 해요!
🔗 관련 자료
📚 이전 학습
Day 10: 미니 프로젝트 ⭐⭐⭐
어제는 Phase 1을 마무리하며 숫자 맞추기 게임을 만들었습니다!
📚 다음 학습
Day 12: 딕셔너리 다루기 (dict) ⭐⭐
내일은 키-값 쌍으로 데이터를 저장하는 딕셔너리를 배웁니다!
“늦었다고 생각할 때가 가장 빠른 시기입니다!” 🚀
| Day 11/100 | Phase 2: 자료형 마스터하기 | #100DaysOfPython |