지식
Python
집합과 frozenset: 중복 없는 컬렉션
Python set의 생성·집합 연산(합·교·차·대칭차)·활용 패턴과, 불변 집합 frozenset을 정리합니다.
지난 글에서 딕셔너리를 살펴봤다. 이번에는 집합(set)을 다룬다. 딕셔너리와 같은 해시 테이블 구조지만 키만 있고 값은 없는 컬렉션이다.
set이란
set은 순서 없음·중복 없음·해시 가능 원소만 허용하는 가변 컬렉션이다. 원소 존재 여부를 O(1)에 확인할 수 있다.
s = {1, 2, 3}
s2 = set([1, 2, 2, 3, 3]) # {1, 2, 3} — 중복 제거
empty = set() # {} 는 빈 dict!
원소 추가·삭제
s = {1, 2, 3}
s.add(4) # {1, 2, 3, 4}
s.discard(99) # 없어도 오류 없음
s.remove(1) # 없으면 KeyError
s.pop() # 임의 원소 제거 (순서 없음)
s.clear() # 모두 제거
2 in s # O(1) 멤버십 테스트
집합 연산
A = {1, 2, 3}
B = {3, 4, 5}
A | B # {1, 2, 3, 4, 5} 합집합
A & B # {3} 교집합
A - B # {1, 2} 차집합 (A에만 있는 것)
B - A # {4, 5}
A ^ B # {1, 2, 4, 5} 대칭차 (합집합 - 교집합)
# 메서드 방식도 가능
A.union(B)
A.intersection(B)
A.difference(B)
A.symmetric_difference(B)
# in-place 버전
A |= B # A.update(B)
A &= B # A.intersection_update(B)
A -= B # A.difference_update(B)
부분집합·상위집합
{1, 2} <= {1, 2, 3} # True — 부분집합
{1, 2} < {1, 2, 3} # True — 진부분집합
{1, 2, 3} >= {1, 2} # True — 상위집합
A.issubset(B)
A.issuperset(B)
A.isdisjoint(B) # 교집합 없으면 True
frozenset — 불변 집합
frozenset은 수정 불가능한 집합이다. 해시 가능하므로 dict 키나 다른 set의 원소로 쓸 수 있다.
fs = frozenset({1, 2, 3})
fs.add(4) # AttributeError — 수정 불가
d = {fs: "label"} # dict 키 가능
s = {frozenset({1, 2}), frozenset({3, 4})} # set의 원소 가능
# 집합 연산 결과는 frozenset 유지
fs | frozenset({4}) # frozenset({1, 2, 3, 4})
실전 패턴
# 리스트 중복 제거
unique = list(set([1, 2, 2, 3, 1])) # 순서 비보장
# 두 리스트의 공통 원소
common = set(a) & set(b)
# 차이 원소
only_in_a = set(a) - set(b)
# 대용량 멤버십 테스트
allowed = {"admin", "user", "guest"} # 리터럴이 set
if role in allowed: # O(1), 리스트면 O(n)
...
set 컴프리헨션
squares = {x**2 for x in range(10)}
# {0, 1, 4, 9, 16, 25, 36, 49, 64, 81}
even = {x for x in range(20) if x % 2 == 0}
주의사항
set은 해시 테이블 기반이라 순서가 없다. 같은 원소를 담아도 출력 순서가 다를 수 있다. 순서가 필요하면 sorted(s)로 정렬해야 한다.
s = {3, 1, 2}
list(s) # [1, 2, 3] 이 될 수도, 아닐 수도 (구현 의존)
sorted(s) # [1, 2, 3] (항상 정렬)
지난 글: 딕셔너리 기초: 키-값 매핑의 모든 것
다음 글: 타입 변환: int, str, list, float 상호 변환
읽어주셔서 감사합니다. 😊