collections · itertools
표준 라이브러리 두 보석. 10줄짜리 작업이 1줄로 끝난다.
5편의 list, 6편의 dict 가 입문자의 4대 자료구조였습니다. 그것들로도 거의 모든 일이 되지만, 같은 패턴이 반복되는 작업이 있어요 — "단어 빈도 세기", "키별로 그룹화", "두 리스트 이어 붙이기", "순열·조합 만들기". 직접 짜면 10-20줄, 표준 라이브러리의 collections·itertools 를 쓰면 1-3줄.
19편은 이 두 모듈의 단골 8가지를 정리합니다. 한 번 손에 익으면 자동화 코드 절대량이 30% 가까이 줄어요.
collections — 4가지 똑똑한 자료구조
Counter — 빈도 세기
from collections import Counter
# 문자 빈도
Counter("banana")
# Counter({'a': 3, 'n': 2, 'b': 1})
# 단어 빈도
text = "the quick brown fox jumps over the lazy dog the".split()
Counter(text).most_common(3)
# [('the', 3), ('quick', 1), ('brown', 1)]
# 두 Counter 의 차이·합
c1 = Counter("apple")
c2 = Counter("ape")
c1 - c2 # Counter({'p': 1, 'l': 1})
c1 + c2 # Counter({'a': 2, 'p': 3, 'l': 1, 'e': 2})
일반 dict 로 짜면 6줄이 한 줄. 로그 분석·텍스트 통계의 표준 도구입니다.
defaultdict — 빈 값 자동 초기화
from collections import defaultdict
# 보통 dict — KeyError 방지하려고 매번 조건문
groups = {}
for w in words:
first = w[0]
if first not in groups:
groups[first] = []
groups[first].append(w)
# defaultdict — 한 줄
groups = defaultdict(list)
for w in words:
groups[w[0]].append(w)
# 없는 키 접근하면 빈 list 가 자동 생성
그룹화·카운팅·중첩 dict 만들기에서 빛납니다. 기본값 함수를 전달하면 그 함수의 결과로 자동 초기화. defaultdict(int) 면 카운터, defaultdict(list) 면 리스트 모음.
deque — 양쪽 끝에서 빠른 큐
from collections import deque
q = deque([1, 2, 3])
q.append(4) # 뒤에 추가 — list 와 같음
q.appendleft(0) # 앞에 추가 — list 는 O(n), deque 는 O(1)
q.popleft() # 앞에서 꺼냄 — list 보다 훨씬 빠름
# deque([1, 2, 3, 4])
# 너비 우선 탐색(BFS)·캐시 라인·최근 N개 같은 곳에 표준
recent = deque(maxlen=5) # 항상 마지막 5개만 유지
for n in range(10):
recent.append(n)
list(recent) # [5, 6, 7, 8, 9]
namedtuple — 가벼운 클래스 대안
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)
p.x, p.y # (3, 4)
p[0], p[1] # (3, 4) — 튜플처럼도 됨
# 14편 dataclass 와 비슷한데 더 가볍고 불변
itertools — 반복의 시그니처
chain — 여러 리스트를 한 줄로
from itertools import chain
a = [1, 2, 3]
b = [4, 5]
c = [6, 7, 8]
list(chain(a, b, c)) # [1, 2, 3, 4, 5, 6, 7, 8]
# 또는 chain.from_iterable([a, b, c])
# 메모리 효율적 — 실제로 새 리스트를 만들지 않음
for x in chain(a, b, c):
print(x)
groupby — 정렬된 데이터의 키별 그룹
from itertools import groupby
data = [
("A", 88), ("A", 92),
("B", 75), ("B", 80), ("B", 91),
("C", 60),
]
# 첫 요소(학점)로 그룹화 — 입력은 그 키로 미리 정렬되어 있어야
for k, group in groupby(data, key=lambda x: x[0]):
scores = [s for _, s in group]
print(f"{k}: 평균 {sum(scores)/len(scores):.1f}")
# A: 평균 90.0
# B: 평균 82.0
# C: 평균 60.0
[A, B, A, B] 를 그대로 넣으면 4 그룹이 됩니다. 같은 키를 한 곳에 모으려면 sorted() 로 먼저 정렬.
combinations·permutations·product — 조합론
from itertools import combinations, permutations, product
list(combinations([1,2,3,4], 2)) # 순서 무관, 중복 없음
# [(1,2), (1,3), (1,4), (2,3), (2,4), (3,4)]
list(permutations([1,2,3], 2)) # 순서 있음
# [(1,2), (1,3), (2,1), (2,3), (3,1), (3,2)]
list(product([1,2], ["a","b"])) # 모든 조합 (곱집합)
# [(1,'a'), (1,'b'), (2,'a'), (2,'b')]
cycle·count·islice — 무한 반복의 안전한 제어
from itertools import cycle, count, islice
# 라운드 로빈
colors = cycle(["red", "green", "blue"])
[next(colors) for _ in range(7)]
# ['red', 'green', 'blue', 'red', 'green', 'blue', 'red']
# 무한 카운터에서 잘라내기
list(islice(count(1, 2), 5)) # 1부터 2씩, 5개
# [1, 3, 5, 7, 9]
📌 itertools 의 도구들은 거의 다 "이터레이터" 반환
실제 리스트가 아니라 "필요할 때 하나씩 꺼내는 발생기". 메모리 안전하지만 print(chain(a, b)) 하면 객체 주소만 보입니다. list(...) 로 펼치거나 for 로 순회.
마무리 — 알면 강력한 단축
이 8가지를 외울 필요는 없어요. "빈도 세야 한다 → Counter", "그룹화 → groupby (또는 defaultdict)", "여러 리스트 합치기 → chain" 같은 트리거만 머리에 있으면 됩니다. 실제 작업하다 "이거 직접 짜기 좀 길다" 싶으면 collections·itertools 를 한 번 떠올려보세요. 90% 답이 있어요.
다음 미션: ① 본인 일주일 매출 데이터에서 가장 많이 팔린 상품 Top 3 (Counter) ② 학생 리스트를 학점별로 그룹화 (defaultdict 또는 groupby) ③ 1~10 중 2개씩 조합 모두 출력 (combinations).
다음 편 미리보기
20편 — "파이썬 requests": 외부 패키지 1번 — HTTP 클라이언트. API 호출·웹 응답 받기.
← 18편 "타입 힌트" · 다음: 20편 "requests 사용법"