in보다 더 나은 선택: 파이썬 딕셔너리 실전 패턴 모음

Sue·2025년 8월 7일
0

Effective Python

목록 보기
9/9
post-thumbnail

in을 사용하고 딕셔너리 키가 없을 때 KeyError를 처리하기보다는 get을 사용하라

딕셔너리에서 키가 존재하지 않을 수 있는 상황을 처리하는 방법은 여러 가지가 있습니다. 각 방법의 장단점을 이해하고 상황에 맞는 최적의 방법을 선택하는 것이 중요합니다.

1. 딕셔너리 키 존재 여부 확인의 다양한 방법

기본 상황 설정

counters = {
    '품퍼니켈': 2,
    '사워도우': 1,
}
key = '밀'  # 존재하지 않는 키

2. 방법 1: in 연산자 사용

if key in counters:
    count = counters[key]
else:
    count = 0

counters[key] = count + 1

특징:

  • 가장 직관적이고 이해하기 쉬운 방법
  • 두 번의 딕셔너리 접근이 필요 (in 검사 + 실제 접근)
  • 코드가 길어질 수 있음

3. 방법 2: try/except 사용

try:
    count = counters[key]
except KeyError:
    count = 0

counters[key] = count + 1

특징:

  • EAFP(Easier to Ask for Forgiveness than Permission) 방식
  • 키가 존재할 가능성이 높을 때 효율적
  • 예외 처리 오버헤드가 있을 수 있음

4. 방법 3: get() 메서드 사용 ⭐ (권장)

count = counters.get(key, 0)
counters[key] = count + 1

장점:

  • 가장 간결하고 pythonic한 방법
  • 한 줄로 해결 가능
  • 딕셔너리 접근이 한 번만 필요
  • 기본값을 명시적으로 지정 가능

5. 카운터 패턴의 진화

5-1. not in 패턴

if key not in counters:
    counters[key] = 0

counters[key] += 1

5-2. in/else 패턴

if key in counters:
    counters[key] += 1
else:
    counters[key] = 1

5-3. try/except 패턴

try:
    counters[key] += 1
except KeyError:
    counters[key] = 1

분석:

  • 모든 방법이 동일한 결과를 만들어내지만
  • 코드의 명확성과 효율성 면에서 차이가 있습니다

6. 복잡한 기본값 처리: 리스트 예제

기본 상황

votes = {
    '바게트': ['철수', '순이'],
    '치아바타': ['하니', '유리'],
}
key = '브리오슈'  # 새로운 빵 종류
who = '단이'

6-1. if/else 패턴

if key in votes:
    names = votes[key]
else:
    votes[key] = names = []

names.append(who)

6-2. try/except 패턴

try:
    names = votes[key]
except KeyError:
    votes[key] = names = []

names.append(who)

6-3. get() with None 체크

names = votes.get(key)
if names is None:
    votes[key] = names = []

names.append(who)

6-4. 월러스 연산자 활용 (Python 3.8+)

if (names := votes.get(key)) is None:
    votes[key] = names = []

names.append(who)

7. setdefault() 메서드: 궁극의 해결책 ⭐⭐

names = votes.setdefault(key, [])
names.append(who)

setdefault()의 동작:

  • 키가 존재하면: 기존 값을 반환
  • 키가 존재하지 않으면: 기본값을 설정하고 그 값을 반환
  • 한 줄로 모든 처리가 완료됨

8. setdefault() 사용 시 주의사항

data = {}
key = 'foo'
value = []
data.setdefault(key, value)
print('이전:', data)    # {'foo': []}
value.append('hello')
print('이후: ', data)    # {'foo': ['hello']}

중요한 포인트:

  • setdefault()는 기본값 객체를 직접 딕셔너리에 저장합니다
  • 반환된 객체와 딕셔너리에 저장된 객체는 같은 객체입니다
  • 뮤터블 객체(리스트, 딕셔너리 등)를 기본값으로 사용할 때 이를 반드시 고려해야 합니다

9. setdefault()가 적합하지 않은 경우

count = counters.setdefault(key, 0)
counters[key] = count + 1

문제점:

  • 카운터처럼 값을 즉시 수정하는 경우
  • setdefault()로 가져온 후 다시 할당하는 것은 비효율적
  • 이런 경우는 get() 방법이 더 적합합니다

10. 상황별 최적 선택 가이드

Simple 값 처리 (int, str 등)

# ✅ 권장: get() 사용
count = counters.get(key, 0) + 1
counters[key] = count

복잡한 객체 처리 (list, dict 등)

# ✅ 권장: setdefault() 사용
names = votes.setdefault(key, [])
names.append(who)

기본값이 복잡한 계산을 요구하는 경우

# ✅ 권장: if/get() 조합
if key not in expensive_cache:
    expensive_cache[key] = expensive_computation()
result = expensive_cache[key]

11. 실제 활용 예시

# 단어 빈도 계산
def count_words(text):
    word_counts = {}
    for word in text.split():
        word_counts[word] = word_counts.get(word, 0) + 1
    return word_counts

# 그룹별 분류
def group_by_category(items):
    groups = {}
    for item in items:
        category = item.get_category()
        groups.setdefault(category, []).append(item)
    return groups

# 중첩 딕셔너리 처리
def nested_dict_access(data, key1, key2):
    return data.get(key1, {}).get(key2, 'default_value')

최종 정리

딕셔너리 키 처리의 핵심 원칙:

  1. 단순한 값: dict.get(key, default) 사용
  2. 복잡한 객체: dict.setdefault(key, default) 사용
  3. KeyError 예외 처리보다는 명시적 기본값 처리를 선호
  4. 코드의 의도를 명확하게 드러내는 방법 선택

get()setdefault()를 적절히 활용하면:

  • 코드가 더 간결해집니다
  • KeyError 예외를 방지할 수 있습니다
  • 의도가 명확하게 드러납니다
  • 성능도 향상됩니다

특히 데이터 집계, 그룹화, 캐싱 등의 작업에서 이러한 패턴들이 매우 유용합니다.

profile
AI/ML Engineer

0개의 댓글