betterway17defaultdict사용하라

김승환·2021년 7월 11일

코딩의 기술

목록 보기
8/36

내부 상태에서 원소가 없는 경우를 처리할 때는 setdefault보다 defaultdict를 사용하라

get 메서드를 사용하는 방법이 in과 KeyError를 사용하는 방법보다 낫지만, 경우에 따라서 setdefault가 가장 빠른 지름길일 수도 있다

visits = {
    '미국': {'뉴욕', '로스엔젤레스'},
    '일본': {'하코네'},
}

#짧은 예시
visits.setdefault('프랑스', set()).add('칸') # 짧다

#긴 예시
if (japan := visits.get('일본')) is None:      # 길다
    visits['일본'] = japan = set()

japan.add('교토')

print(visits)

{'미국': {'뉴욕', '로스엔젤레스'}, '일본': {'하코네', '교토'}, '프랑스': {'칸'}}

직접 딕셔너리 생성을 제어할 수 있다면 어떨까?

  • 예를 들어 클래스 내부에서 상태를 유지하기 위해 직셔너리 인스턴스를 사용할 때가 이런 경우이다.
  • 아래 예시는 앞에서 본 예제를 클래스로 감싸서 딕셔너리에 저장된 동적인 내무 상태에 접근할 수 있는 도우미 메서드를 제공한다.
class Visits:
    def __init__(self):
        self.data = {}

    def add(self, country, city):
        city_set = self.data.setdefault(country, set())
        city_set.add(city)
#새로만든 이 클래스는 setdefault 호출의 복잡도를 제대로 감춰주며 더 나은 인터페이스를 제공
# 위 클래스를 이용하여 간편하게 딕셔너리를 편집하는 예시
visits = Visits()
visits.add('러시아', '예카테린부르크')
visits.add('탄자니아', '잔지바르')
print(visits.data)

{'러시아': {'예카테린부르크'}, '탄자니아': {'잔지바르'}}

  • setdefault라는 메서드 이름은 여전히 헷갈리고 주어진 나라가 data 딕셔너리에 있는 없든 호출 할 때마다 새로 set 인스턴스를 만들기 때문에 효율적이지 않다.

collections 내장 모듈에 있는 defaultdict 클래스는 키가 없을 때 자동으로 디폴트 값을 저장해서 이런 용법을 간단하게 처리가능.

  • 키가 없을 때 디폴트 값을 만들기 위해 호출할 함수를 제공하기만 하면 가능.
# default를 사용하여 Visits클래스를 다시 작성한 것,
from collections import defaultdict

class Visits:
    def __init__(self):
        self.data = defaultdict(set)

    def add(self, country, city):
        self.data[country].add(city)

visits = Visits()
visits.add('영국', '바스')
visits.add('영국', '런던')
print(visits.data)

defaultdict(<class 'set'>, {'영국': {'런던', '바스'}})

  • add 코드는 data 딕셔너리에 있는 키에 접근하면 항상 기존 set 인스턴스가 반환된다고 가정한다.
  • add 메서드가 아주 많이 호출되면 집합 생성에 따른 비용도 커지는데, 이 구현에서 불필요한 set이 만들어지는 경우는 없다.

visits.add('미국', '바보')

print(visits.data)

defaultdict(<class 'set'>, {'영국': {'런던', '바스'}, '미국': {'바보'}})

profile
인공지능 파이팅!

0개의 댓글