포스트

[Python 100일 챌린지] Day 11 - 튜플 다루기 (tuple)

[Python 100일 챌린지] Day 11 - 튜플 다루기 (tuple)

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
최소값: 1, 최대값: 9

* 연산자로 나머지 받기

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
2
3
0: 사과
1: 바나나
2: 포도

💻 실전 예제

예제 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점)

📝 오늘 배운 내용 정리

  1. 튜플 정의: 수정 불가능한(immutable) 순서가 있는 데이터 모음
  2. 리스트와 차이: 튜플은 변경 불가, 리스트는 변경 가능
  3. 튜플 생성: (), 쉼표, tuple() 함수 사용
  4. 튜플 언패킹: 여러 값을 한 번에 변수에 할당
  5. 활용: 좌표, 색상, 함수 반환값, 딕셔너리 키 등

핵심 포인트:

  • 튜플은 한 번 만들면 절대 변경할 수 없습니다
  • 단일 요소 튜플은 쉼표 필수: (5,)
  • 리스트보다 빠르고 메모리 효율적
  • 변경하면 안 되는 데이터에 튜플 사용

💬 자주 묻는 질문

Q. 튜플과 리스트 중 뭘 써야 하나요? → 데이터가 변경되면 안 되면 튜플, 변경이 필요하면 리스트!

Q. 튜플은 왜 수정이 안 되나요? → 데이터 보호와 성능 향상을 위해 설계되었습니다.

Q. 튜플을 리스트로 바꿀 수 있나요? → 네! list(my_tuple)로 변환 가능합니다.

Q. 단일 요소 튜플에 왜 쉼표가 필요한가요?(5)는 숫자 5이고, (5,)가 튜플입니다. 쉼표로 구분해야 해요!


🔗 관련 자료


📚 이전 학습

Day 10: 미니 프로젝트 ⭐⭐⭐

어제는 Phase 1을 마무리하며 숫자 맞추기 게임을 만들었습니다!

📚 다음 학습

Day 12: 딕셔너리 다루기 (dict) ⭐⭐

내일은 키-값 쌍으로 데이터를 저장하는 딕셔너리를 배웁니다!


“늦었다고 생각할 때가 가장 빠른 시기입니다!” 🚀

Day 11/100 Phase 2: 자료형 마스터하기 #100DaysOfPython
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.