[Python] set, frozenset
python set, frozenset, 파이썬 집합, set operations
정의
set은 중복 없는 해시 가능 원소의 가변 컬렉션이다. dict의 키 슬롯만 가진 형태로 구현되어 평균 O(1) 멤버십·삽입·삭제를 제공한다. frozenset은 그 불변 버전으로 해시 가능하다.
생성
empty = set() # {} 는 dict! 반드시 set()
s = {1, 2, 3}
from_list = set([1, 2, 2, 3]) # {1, 2, 3} 중복 제거
comp = {x ** 2 for x in range(5)}
fs = frozenset([1, 2, 3])
함정: {}는 빈 dict이지 빈 set이 아니다.
기본 연산
python
s = {1, 2, 3}
s.add(4)
s.add(2) # 중복 무시
print(s)
s.remove(3) # 없으면 KeyError
s.discard(99) # 없어도 조용
print(s)
print(2 in s) # 멤버십 O(1)
print(len(s)) 결과
{1, 2, 3, 4}
{1, 2, 4}
True
3집합 연산
| 연산 | 기호 | 메서드 | 의미 |
|---|---|---|---|
| 합집합 | | | .union() | 둘 중 하나 |
| 교집합 | & | .intersection() | 둘 다 |
| 차집합 | - | .difference() | A에만 |
| 대칭차 | ^ | .symmetric_difference() | 한쪽에만 |
| 부분집합 | <= | .issubset() | A ⊆ B |
| 진부분집합 | < | A ⊊ B | |
| 상위집합 | >= | .issuperset() | A ⊇ B |
| 서로소 | .isdisjoint() | 교집합 없음 |
python
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
print(a | b)
print(a & b)
print(a - b)
print(a ^ b)
print({1, 2}.issubset(a))
print(a.isdisjoint({99, 100})) 결과
{1, 2, 3, 4, 5, 6}
{3, 4}
{1, 2}
{1, 2, 5, 6}
True
True제자리 갱신
a = {1, 2, 3}
a |= {3, 4} # 합집합 in-place: {1, 2, 3, 4}
a &= {2, 3, 4} # 교집합 in-place: {2, 3, 4}
a -= {2} # 차집합 in-place: {3, 4}
a ^= {3, 99} # 대칭차 in-place: {4, 99}
# 메서드 버전
a.update({5, 6}) # |=
a.intersection_update()
a.difference_update()
a.symmetric_difference_update()
메서드와 메서드 인자 차이
연산자(|, &)는 양쪽 모두 set 타입이어야 한다. 메서드는 어떤 iterable이든 받는다.
s = {1, 2, 3}
s | [4, 5] # TypeError
s.union([4, 5]) # OK: {1, 2, 3, 4, 5}
s.intersection_update("abc1") # 문자열도 iter 가능
frozenset: 해시 가능 set
frozenset은 변경 불가라서 dict 키 / 다른 set의 원소로 쓸 수 있다.
python
# 그래프 무방향 엣지 표현
edges = {
frozenset({"A", "B"}),
frozenset({"B", "C"}),
frozenset({"A", "B"}), # 중복
}
print(len(edges)) # 2
# dict 키로
permissions = {
frozenset({"read"}): "viewer",
frozenset({"read", "write"}): "editor",
}
print(permissions[frozenset({"read", "write"})]) 결과
2
editor흔한 활용 패턴
중복 제거 (순서 무관)
unique = list(set([1, 2, 2, 3, 1])) # [1, 2, 3] (순서 보장 X)
# 순서 보장 필요 시 → dict.fromkeys
unique = list(dict.fromkeys([1, 2, 2, 3, 1])) # [1, 2, 3]
빠른 멤버십 검사
banned = set(load_blacklist()) # list 대신 set
for user in users:
if user.id in banned: # O(1)
...
두 컬렉션 비교
old = {"a", "b", "c"}
new = {"b", "c", "d"}
added = new - old # {"d"}
removed = old - new # {"a"}
unchanged = old & new # {"b", "c"}
set vs dict vs list
| 상황 | 선택 |
|---|---|
| 멤버십 검사 O(1) | set |
| 키-값 매핑 | dict |
| 순서 보장, 중복 허용 | list |
| 해시 가능한 불변 모음 | frozenset |
| 순서 + 중복 제거 | dict.fromkeys() |
함정 정리
{}는 dict, 빈 set은set()- set은 순서 없음(3.7+ dict와 달리 보장 안 함)
- 원소는 해시 가능해야 함 (list, dict 불가)
- set 자체는 hashable 아님 (frozenset만)
💬 댓글