set 은 순서 없는, 유니크하고 hashable 한 원소들의 컬렉션. 일상 코드에서 list 나 dict 만큼 자주는 안 써. 근데 필요할 땐 다른 컬렉션이 못 따라와 — dedup, 빠른 멤버십 테스트, 그리고 4 가지 set 연산 (합집합, 교집합, 차집합, 대칭차). 리터럴은 {1, 2, 3}. 빈 set 은 set() — {} 는 빈 dict 라서.
O(1) 멤버십이 왜 큰가
x in my_list 는 O(n) — list 를 다 걸어. x in my_set 은 O(1) — 해시 조회. "이 안에 있냐" 를 수천 번 묻는 코드라면, 컬렉션을 list 에서 set 으로 바꾸는 게 알고리즘 수정 없이 가장 큰 성능 향상이야.
set 수학 — 합집합 / 교집합 / 차집합
set 은 진짜 수학적 set 연산을 지원해. 연산자도 있고 메서드 이름도 있어. a | b (또는 a.union(b)) — 어느 한쪽에라도 있는 것. a & b (교집합) — 양쪽 다. a - b (차집합) — a 에 있고 b 에 없는 것. a ^ b (대칭차) — 정확히 한쪽에만. 메서드는 어떤 iterable 도 받지만, 연산자는 set 끼리만.
원칙: "A 에 있고 B 에 없는 거" 찾으려고 nested for 문 쓰려는 충동이 들면 멈추고 set(A) - set(B). 이 패턴 인식 익히는 데 몇 달 걸리는데, 익히면 코드가 통째로 변해.
set 은 순서가 없어 — 그게 버그가 아니야
set 은 삽입 순서 안 지켜. 순회하면 어떤 순서로 나옴 — 보통 (항상은 X) 해시 순서. 순서가 중요하면 list 써. 유니크 + 순서 둘 다 원하면 현대 idiom 은 list(dict.fromkeys(items)) — dict 가 삽입 순서니까.
frozenset — set 을 hashable 로
일반 set 은 mutable 이라 dict 키도 안 되고 다른 set 안에도 못 들어가. frozenset 이 immutable 버전. 같은 연산, .add()/.remove() 없음, hashable. use case — 순서 없는 컬렉션을 키로 쓰고 싶을 때 (다태그 조합, 그래프 엣지를 frozenset({u, v}) 로).
Code
set 만들기 — 빈 set 의 함정·python
# 리터럴
s = {1, 2, 3}
# iterable 에서 — dedup 도 해줘
s = set([1, 2, 2, 3, 3, 3])
print(s) # {1, 2, 3}
# 빈 set — {} 가 아니라 set()
empty = set() # 빈 SET
empty_dict = {} # 빈 DICT
print(type(empty)) # <class 'set'>
print(type(empty_dict)) # <class 'dict'>
멤버십 — O(1) vs O(n)·python
import time
big_list = list(range(1_000_000))
big_set = set(big_list)
start = time.perf_counter()
result = 999_999 in big_list # O(n)
print("list:", time.perf_counter() - start)
start = time.perf_counter()
result = 999_999 in big_set # O(1)
print("set:", time.perf_counter() - start)
# list 가 보통 수천 배 느림
set 수학 — 연산자와 메서드·python
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
# 합집합
print(a | b) # {1, 2, 3, 4, 5, 6}
print(a.union(b)) # 같음
# 교집합
print(a & b) # {3, 4}
print(a.intersection(b)) # 같음
# 차집합
print(a - b) # {1, 2}
print(a.difference(b)) # 같음
# 대칭차 — 정확히 한쪽에만
print(a ^ b) # {1, 2, 5, 6}
# 부분집합 / 상위집합
print({1, 2} <= a) # True
print(a >= {1, 2}) # True
순서 유지하면서 dedup — dict 트릭·python
items = ["apple", "banana", "apple", "cherry", "banana"]
# set() 은 dedup 하지만 순서 잃어
print(set(items)) # 순서 안 정해짐
# 유니크 + 순서? dict.fromkeys() — dict 는 순서 보장
print(list(dict.fromkeys(items))) # ['apple', 'banana', 'cherry'] — 첫 등장 순서
frozenset — hashable 이라 키로 가능·python
# 일반 set 은 hashable X
edge_lookup = {}
try:
edge_lookup[{1, 2}] = "edge"
except TypeError as e:
print(e) # unhashable type: 'set'
# frozenset 은 hashable
edge_lookup[frozenset({1, 2})] = "edge A"
edge_lookup[frozenset({2, 3})] = "edge B"
# 순서 무관 — 같은 엣지
print(edge_lookup[frozenset({2, 1})]) # 'edge A'
두 list — users_2024 = ['alice', 'bob', 'charlie', 'dave'], users_2025 = ['bob', 'charlie', 'eve', 'frank']. 명시적 loop 없이 (a) 두 해 모두 활동, (b) 2024 만, (c) 2025 만, (d) 정확히 한 해만 활동 — 계산 + 출력. 출력 전 정렬된 list 로 변환.
Progress
Progress is local-only — sign in to sync across devices.