튜플과 집합
여러분은 이미 리스트를 알고 있습니다. Python에는 리스트로 해결할 수 없는 문제들을 풀어주는 두 가지 컬렉션 타입이 더 있습니다. 튜플은 절대 변하지 않는 고정된 값들의 묶음을 담습니다. 집합은 고유한 값만 담으며, 컬렉션이 아무리 커져도 멤버십을 즉시 확인할 수 있게 해줍니다.
튜플
튜플은 생성한 후에는 변경할 수 없는 순서가 있는 값들의 묶음입니다. 괄호는 튜플을 정의하지만, 선택 사항입니다. 실제로 튜플을 만드는 것은 쉼표입니다. 단일 항목 튜플에는 끝에 쉼표가 필요합니다.
point = (10, 20)
rgb = (255, 128, 0)
dimensions = (1920, 1080)
single = (42,) # 단일 항목 튜플에는 끝의 쉼표가 필요
also_tuple = 42, 99 # 괄호는 선택 사항; 쉼표가 튜플을 만듭니다인덱스로 접근하는 것은 리스트와 정확히 같은 방식으로 작동합니다. 항목을 변경하려고 하면 TypeError가 발생합니다:
point = (10, 20)
point[0] # 10
point[1] # 20
point[-1] # 20
point[0] = 99 # TypeError: 'tuple' object does not support item assignment튜플을 언제 사용할까
함께 속하며 변하지 않을 작은 관련 값들의 묶음이 있을 때 튜플을 사용하세요. 좌표 (x, y), 색상 (r, g, b), 이름-점수 쌍 ("민수", 87). 고정된 구조는 코드를 읽는 사람에게 이 그룹이 단일 단위로 취급된다는 신호를 보냅니다.
locations = {}
locations[(40, -74)] = "서울" # 튜플을 딕셔너리 키로, 작동함
locations[[40, -74]] = "서울" # 리스트를 딕셔너리 키로, TypeError언패킹
언패킹은 튜플에서 값을 꺼내어 각각을 한 줄에 자기 이름에 할당합니다. 이름의 수는 값의 수와 일치해야 합니다. 남은 항목들을 리스트로 캡처하려면 *를 사용하세요.
point = (10, 20)
x, y = point
print(x) # 10
print(y) # 20
first, *rest = [1, 2, 3, 4, 5]
# first = 1, rest = [2, 3, 4, 5]
head, *middle, tail = [1, 2, 3, 4, 5]
# head = 1, middle = [2, 3, 4], tail = 5명명된 튜플
명명된 튜플은 각 위치에 이름이 있는 튜플입니다. point[0]이 x 좌표라는 것을 기억하는 대신, point.x라고 쓸 수 있습니다. 값들은 여전히 불변입니다; 단지 숫자 위치 대신 읽기 쉬운 속성 이름을 얻을 뿐입니다.
명명된 튜플 임포트
namedtuple은 Python 표준 라이브러리에 있지만 임포트가 필요합니다. from collections import namedtuple 줄은 이 코스에서 첫 번째 임포트입니다. 임포트는 모듈 챕터에서 완전히 다룹니다.
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
Player = namedtuple("Player", ["name", "score", "level"])
p = Point(10, 20)
p.x # 10
p.y # 20
minsu = Player("민수", 87, 5)
minsu.name # "민수"
minsu.score # 87집합
집합은 보장된 순서가 없는 고유 값들의 컬렉션입니다. 같은 값을 두 번 추가해도 아무 일도 일어나지 않습니다: 집합은 각 항목의 사본을 하나만 유지합니다. 항목이 있는 집합에는 중괄호를 사용하고, 빈 집합을 만들려면 set()을 사용하세요.
tags = {"python", "beginner", "tutorial"}
numbers = {1, 2, 3, 4, 5}
empty = set() # {}가 아님 (그것은 빈 딕셔너리)같은 값을 두 번 추가해도 집합은 변경되지 않습니다:
tags.add("python") # tags는 변경되지 않음, "python"이 이미 있음집합을 언제 사용할까
집합은 세 가지에 적합한 도구입니다: 리스트에서 중복 제거하기, 큰 컬렉션에서 무언가가 있는지 빠르게 확인하기, 그리고 두 그룹을 비교하여 공유하거나 다른 점을 찾기.
# 리스트에서 중복 제거
raw = ["cat", "dog", "cat", "bird", "dog", "cat"]
unique = list(set(raw)) # ["cat", "dog", "bird"] (순서는 보장되지 않음)# 빠른 멤버십 확인
valid_codes = {"USD", "EUR", "GBP", "JPY"}
code = "EUR"
if code in valid_codes: # 수천 개의 코드가 있어도 즉시 조회
print("Valid")집합 연산
집합은 수학에서 배운 것과 동일한 연산을 지원합니다: 합집합(두 집합 중 하나에 있는 모든 것), 교집합(두 집합이 공유하는 것만), 그리고 차집합(한쪽에 있고 다른 쪽에는 없는 것). Python은 이를 위해 연산자 기호를 사용하며, 각각은 메서드 등가물을 가집니다.
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
a | b # {1, 2, 3, 4, 5, 6} (합집합: 어느 한쪽에 있는 모든 것)
a & b # {3, 4} (교집합: 둘 다에 있는 것만)
a - b # {1, 2} (차집합: a에 있고 b에는 없는 것)
b - a # {5, 6} (반대 방향 차집합)
a ^ b # {1, 2, 5, 6} (대칭차: 둘 중 하나에만 있는 것)이들에는 메서드 형태도 있습니다: .union(), .intersection(), .difference(), .symmetric_difference().
집합 수정하기
집합은 가변입니다. .add()는 항목 하나를 추가합니다. .update()는 모든 리스트나 다른 이터러블에서 여러 개를 한 번에 추가합니다. .remove()는 항목을 삭제하지만 없으면 오류를 발생시킵니다. .discard()는 항목이 존재하면 조용히 삭제하고 없으면 아무것도 하지 않습니다.
tags = {"python", "beginner"}
tags.add("tutorial") # 항목 하나 추가
tags.update(["web", "api"]) # 모든 이터러블에서 여러 항목 추가
tags.remove("beginner") # 제거, 없으면 KeyError 발생
tags.discard("missing") # 제거, 없어도 오류 없음
tags.pop() # 임의의 항목을 제거하고 반환
tags.clear() # 모두 제거항목이 존재하는지 확실하지 않을 때는 .discard()를 사용하세요.
동결 집합
동결 집합은 생성 후에 수정할 수 없는 집합입니다. 사용하는 주된 이유는: 동결 집합은 해시 가능하므로 딕셔너리 키로 사용하거나 다른 집합 안에 저장할 수 있습니다.
valid_statuses = frozenset({"active", "paused", "deleted"})
valid_statuses.add("archived") # AttributeError, frozenset은 불변올바른 컬렉션 선택하기
네 가지 타입, 각각 명확한 역할이 있습니다. 데이터로 무엇을 해야 하는지 묻고, 올바른 선택이 보통 명확해집니다.
| list | tuple | set | dict | |
|---|---|---|---|---|
| 순서 | 있음 | 있음 | 없음 | 있음 (삽입 순서) |
| 가변 | 가능 | 불가능 | 가능 | 가능 |
| 중복 | 허용 | 허용 | 불가 | 불가 (키) |
| 접근 방법 | 인덱스 | 인덱스 | 해당 없음 | 키 |
| 사용 시점 | 순서가 있고 변경 가능한 시퀀스 | 고정 레코드 | 고유 값, 빠른 멤버십 | 키-값 조회 |
빠른 결정 규칙:
- 이름으로 무언가를 조회해야 하나요? → dict
- 수정할 순서가 있는 컬렉션이 필요한가요? → list
- 관련 값들의 고정된 묶음이 있나요? → tuple
- 고유 값이나 빠른 멤버십 테스트가 필요한가요? → set
실전에서
튜플을 사용하여 고정된 레코드를 저장하고 집합을 사용하여 고유한 값을 추적하기:
home = (37.5665, 126.9780) # 위도, 경도
office = (37.5172, 127.0473)
home_lat, home_lon = home
print(f"집: {home_lat}, {home_lon}")
# 집합으로 고유 방문자 추적
visitors = set()
visitors.add("민수")
visitors.add("지영")
visitors.add("민수") # 이미 집합에 있음, 조용히 무시됨
visitors.add("수진")
print(f"고유 방문자: {len(visitors)}")
print(f"민수 방문: {'민수' in visitors}")
print(f"준호 방문: {'준호' in visitors}")
