C.W.K.
Stream
Lesson 05 of 07 · published

Set 와 frozenset — 수학 / 멤버십 / dedup

~18 min · set, frozenset, membership, set-math

Level 0호기심
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

덜 자주 쓰지만, 쓸 때는 이거 외엔 답이 없어

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'

External links

Exercise

두 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.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.