
파이썬 defaultdict는 사소한 편의 문법처럼 보이지만, 실제로는 없는 키 초기화 패턴을 한 곳에 모아주는 dict subclass입니다. 그래서 카운팅, 그룹핑, 버킷 만들기 코드가 꽤 많이 단순해집니다.
먼저 핵심부터 말하면 defaultdict는 없는 키를 만났을 때 어떤 기본값을 만들지 default_factory로 미리 선언해두는 구조이며, d[key] 접근에서만 그 자동 생성이 동작합니다.

파이썬 defaultdict를 한 줄로 이해하기
Python 공식 문서는 defaultdict를 missing key에 대해 factory function을 호출해 기본값을 공급하는 dict subclass로 설명합니다. 즉, 일반 dict와 거의 같지만 없는 키를 만났을 때 기본값 생성 규칙이 추가된 구조입니다.
from collections import defaultdict
counts = defaultdict(int)
counts["apple"] += 1
counts["apple"] += 1
print(counts["apple"]) # 2여기서 int는 0을 만들어주는 factory로 쓰입니다. 일반 dict라면 먼저 0으로 초기화하는 분기문이 필요하지만, defaultdict는 그 규칙을 선언 시점에 한 번 적어두면 됩니다.
일반 dict에서는 왜 코드가 길어질까
words = ["apple", "banana", "apple", "orange", "banana", "apple"]
counts = {}
for word in words:
if word not in counts:
counts[word] = 0
counts[word] += 1
print(counts)이 코드는 맞습니다. 하지만 핵심 로직은 개수를 늘리는 것인데, 그 전에 없는 키를 초기화하는 코드가 반복됩니다.
from collections import defaultdict
words = ["apple", "banana", "apple", "orange", "banana", "apple"]
counts = defaultdict(int)
for word in words:
counts[word] += 1
print(counts)defaultdict 버전은 코드의 중심이 더 분명합니다. 단어를 세어라라는 의도가 바로 보입니다.
default_factory와 __missing__은 실제로 어떻게 동작할까
공식 문서 기준으로 default_factory가 None이면 없는 키 접근 시 KeyError가 납니다. None이 아니면 인자 없이 호출해 기본값을 만들고, 그 값을 딕셔너리에 넣은 뒤 반환합니다.
from collections import defaultdict
groups = defaultdict(list)
groups["python"].append("dict")
groups["python"].append("set")
a = groups["java"]
print(a)
print(groups)없는 키를 읽는 순간 기본값이 실제 딕셔너리에 들어간다는 점을 기억하면 동작을 훨씬 덜 헷갈립니다.

defaultdict(int)는 카운팅에서 특히 편하다
from collections import defaultdict
scores = ["A", "B", "A", "C", "B", "A"]
count_by_grade = defaultdict(int)
for grade in scores:
count_by_grade[grade] += 1
print(count_by_grade)여기서 int는 0을 만드는 factory입니다. 처음 보는 키면 0으로 시작하고, 이후에는 그냥 1을 더합니다. 카운팅 코드에서 if문을 없애는 가장 전형적인 패턴입니다.
defaultdict(list)는 그룹핑에서 빛난다
from collections import defaultdict
fruits = ["apple", "apricot", "banana", "blueberry", "avocado"]
groups = defaultdict(list)
for fruit in fruits:
groups[fruit[0]].append(fruit)
print(groups)list factory는 같은 키 아래에 여러 값을 모을 때 자연스럽습니다. append만 생각하면 되기 때문에 그룹핑 의도가 아주 또렷하게 드러납니다.
defaultdict(set)은 중복 없는 버킷을 만들 때 좋다
from collections import defaultdict
pairs = [
("python", "dict"),
("python", "set"),
("python", "dict"),
("java", "map"),
]
tech_map = defaultdict(set)
for language, topic in pairs:
tech_map[language].add(topic)
print(tech_map)set factory는 그룹핑은 하되 중복은 제거하고 싶을 때 잘 맞습니다. list와 set 중 무엇을 고를지는 보존하고 싶은 정보가 순서인지, 중복 없는 membership인지에 따라 달라집니다.
setdefault와는 무엇이 다를까
공식 문서에 따르면 dict.setdefault(key, default)는 키가 있으면 기존 값을 반환하고, 없으면 default를 넣고 그 값을 반환합니다. 그래서 그룹핑 코드를 아래처럼도 쓸 수 있습니다.
fruits = ["apple", "apricot", "banana", "blueberry", "avocado"]
groups = {}
for fruit in fruits:
groups.setdefault(fruit[0], []).append(fruit)
print(groups)setdefault도 충분히 쓸 만합니다. 다만 반복문 안에서 같은 패턴을 계속 적게 되므로, 반복이 길어질수록 defaultdict가 더 읽기 편한 경우가 많습니다.

꼭 알아야 하는 주의점: get()은 default_factory를 쓰지 않는다
Python 공식 문서는 __missing__이 __getitem__ 경로에서만 호출된다고 설명합니다. 그래서 d[key] 접근은 기본값 자동 생성이 가능하지만, d.get(key)는 일반 dict처럼 동작합니다.
from collections import defaultdict
d = defaultdict(list)
print(d.get("python")) # None
print(d) # 아직 비어 있음
print(d["python"]) # []
print(d) # 이제 키가 생김이 차이를 모르면 get도 자동 생성해줄 거라고 기대했다가 헷갈리기 쉽습니다. 조회만 할지, 없으면 실제로 버킷을 만들지 의도를 분명히 나누는 편이 좋습니다.
defaultdict가 항상 정답은 아니다
- 없는 키를 만나는 것 자체가 버그인 경우에는 일반 dict가 더 자연스럽습니다.
- 카운팅만 하고 싶다면 Counter가 더 직접적일 수 있습니다.
- 기본값 규칙이 문맥마다 자주 달라진다면 자동 생성보다 명시적 분기가 더 읽기 쉬울 수 있습니다.
from collections import Counter
words = ["apple", "banana", "apple", "orange", "banana", "apple"]
counts = Counter(words)
print(counts)즉 단순 빈도수 집계는 Counter, 그룹핑/버킷/커스텀 누적은 defaultdict라고 생각하면 꽤 편합니다.
한 번에 기억하는 선택 기준
- 없는 키를 만났을 때 같은 기본값 규칙이 계속 반복되는가?
- 카운팅, 그룹핑, 버킷 생성이 핵심인가?
- if key not in d 또는 setdefault가 반복되어 핵심 로직이 흐려지는가?
이 질문에 예가 많다면 defaultdict가 잘 맞을 가능성이 큽니다. 반대로 없는 키를 만나는 순간 바로 오류를 보고 싶은 경우에는 일반 dict가 더 자연스러울 수 있습니다.
함께 보면 좋은 글과 공식 문서
기본 문법 흐름을 더 정리하고 싶다면 파이썬 pass, continue, break 차이, 파이썬 is와 == 차이도 같이 읽어보면 좋습니다.
공식 기준은 collections.defaultdict 문서와 dict.setdefault 문서를 참고하면 됩니다.
결론: defaultdict는 기본값 규칙을 선언해두는 dict다
파이썬 defaultdict는 없는 키를 만났을 때 어떤 기본값을 만들지 미리 선언해두는 dict subclass입니다. 그래서 카운팅, 그룹핑, 중복 없는 버킷처럼 누락 키 초기화가 반복되는 장면에서 특히 편합니다. 다만 get은 자동 생성하지 않는다는 규칙까지 같이 기억하면 훨씬 안전하게 쓸 수 있습니다.