바쁜 분은 이것만
- 키가 없을 수도 있으면
d.get("키", 기본값)— 없으면 기본값, 에러 없음 - 개수 세기:
cnt[w] = cnt.get(w, 0) + 1또는Counter(words) - KeyError가 났는데 키가 분명 있다면:
print(list(d.keys()))로 공백·대소문자·숫자/문자 확인
파이썬 딕셔너리에서 없는 키를 꺼내면 이런 에러가 납니다.
Traceback (most recent call last):
File "tb.py", line 2, in <module>
print(scores["영희"])
~~~~~~^^^^^^^^
KeyError: '영희'
에러 메시지 마지막 줄의 '영희'가 없는 키 이름입니다. 해결법은 상황마다 다른데, 자주 나오는 경우를 Python 3.14에서 실행한 결과로 정리했어요.
1. [] 대신 get()
scores = {"철수": 85, "민지": 90}
scores["민지"] # 90
scores.get("영희") # None
scores.get("영희", 0) # 0
"영희" in scores # False
d[키]: 키가 없으면 KeyError. 반드시 있어야 하는 키에 씁니다(없으면 오히려 에러가 나야 버그를 찾기 쉬워요).d.get(키, 기본값): 없으면 기본값(안 주면 None). 있어도 되고 없어도 되는 키에 씁니다.키 in d: 있는지만 확인.
주의할 점 하나. get()의 기본값은 키가 없을 때만 나옵니다. 키는 있는데 값이 None이면 None이 그대로 나와요.
d = {"a": None}
d.get("a", "기본값") # None
2. 키가 분명히 있는데 KeyError: 공백·대소문자
row = {"name ": "철수", "Age": 20}
row["name"]
KeyError: 'name'
CSV 헤더나 엑셀에서 읽은 데이터에서 자주 나옵니다. 화면에선 같아 보여도 키 끝에 공백이 붙어 있거나 대문자가 섞여 있어요. 이럴 땐 키 목록을 그대로 찍어 보면 바로 보입니다.
print(list(row.keys()))
['name ', 'Age']
'name ' 뒤의 공백이 보이죠. 읽을 때 {k.strip().lower(): v for k, v in row.items()}처럼 키를 정리해 두면 됩니다.
3. 숫자 키가 JSON을 거치면 문자열이 된다
import json
j = json.loads(json.dumps({1: "one"}))
print(j)
j[1]
{'1': 'one'}
KeyError: 1
JSON은 키를 문자열로만 저장합니다. 파일로 저장했다가 다시 읽으면 1이 '1'이 돼서 j[1]은 KeyError, j["1"]은 됩니다.
4. 개수 세다가 KeyError
words = ["사과", "배", "사과", "감", "사과"]
cnt = {}
for w in words:
cnt[w] += 1
KeyError: '사과'
cnt[w] += 1은 cnt[w] = cnt[w] + 1이라, 처음 나온 단어는 꺼낼 값이 없어서 에러가 납니다. 세 가지 방법으로 고칠 수 있어요.
# ① get으로 기본값 0
cnt = {}
for w in words:
cnt[w] = cnt.get(w, 0) + 1
# ② defaultdict: 없는 키는 int() = 0으로 자동 생성
from collections import defaultdict
dd = defaultdict(int)
for w in words:
dd[w] += 1
# ③ Counter: 세는 전용
from collections import Counter
Counter(words).most_common(2)
{'사과': 3, '배': 1, '감': 1}
{'사과': 3, '배': 1, '감': 1}
[('사과', 3), ('배', 1)]
단순히 세는 거면 Counter가 가장 짧고, most_common(n)으로 많은 순 n개도 바로 나옵니다.
5. 그룹으로 묶을 때: setdefault
groups = {}
for name, team in [("철수", "A"), ("민지", "B"), ("영희", "A")]:
groups.setdefault(team, []).append(name)
print(groups)
{'A': ['철수', '영희'], 'B': ['민지']}
setdefault(키, 기본값)은 키가 없으면 기본값을 넣고, 있으면 기존 값을 돌려줍니다. if team not in groups: groups[team] = [] 두 줄을 한 줄로 줄인 거예요. defaultdict(list)를 써도 같습니다.
6. API 응답처럼 중첩된 딕셔너리
data = {"user": {"profile": {"city": "대구"}}}
data.get("user", {}).get("profile", {}).get("city") # '대구'
data.get("admin", {}).get("profile", {}).get("city") # None
data["admin"]["profile"] # KeyError: 'admin'
중간 단계 기본값을 빈 딕셔너리 {}로 주면 어느 단계에서 비어도 None으로 끝납니다. 다만 너무 많이 쓰면 데이터가 잘못 와도 조용히 None이 돼서 찾기 어려우니, 반드시 있어야 하는 값은 []로 꺼내는 게 낫습니다.
7. pop과 반복 중 삭제
scores.pop("없는키", None) # None
scores.pop("없는키") # KeyError: '없는키'
d = {"a": 1, "b": 2}
for k in d:
if d[k] == 1:
del d[k]
RuntimeError: dictionary changed size during iteration
pop도 기본값을 주면 KeyError가 안 납니다. 반복문 안에서 지우면 KeyError가 아니라 RuntimeError가 나는데, for k in list(d):처럼 키 목록을 복사해서 돌리거나 {k: v for k, v in d.items() if v != 1}로 새로 만드세요.
정리
| 상황 | 쓸 것 |
|---|---|
| 없을 수도 있는 키 | d.get(k, 기본값) |
| 있는지 확인 | k in d |
| 개수 세기 | Counter(list) |
| 그룹 묶기 | d.setdefault(k, []).append(v) |
| 지우기 | d.pop(k, None) |
참고: Python 공식 문서 — Built-in Types: dict (get, setdefault, pop), collections.defaultdict, collections.Counter, json 모듈
