파이썬 defaultdict 사용법
2023년 04월 09일
Python
📕 목차
파이썬에는 해시 테이블 기반의 키-값 쌍으로 이루어진 딕셔너리(Dictionary) 자료형이 있습니다. 파이썬에서 딕셔너리를 사용할 때 마주칠 수 있는 흔한 문제는 딕셔너리에 존재하지 않는 키에 접근할 때 입니다.
이때, 존재하지 않는 키에 접근할 때는 KeyError가 발생합니다.
그래서, 코드를 작성할 때 KeyError의 발생을 방지하기 위해서 사전에 데이터를 만들어주는 작업이 필요합니다. 파이썬에서는 이러한 문제를 해결하기 위해 defaultdict 이라는 dict의 서브 클래스를 제공합니다. 이번 포스팅에서는 defaultdict이 dict와 어떻게 다르고 default_factory를 어떻게 사용하는지 알아보겠습니다.
defaultdict란?
defaultdict은 dict의 서브클래스로 dict와 유사하게 동작하지만, 존재하지 않는 키에 접근할 때 KeyError를 발생시키는 대신에 __missing__
에서 반환하는 팩토리함수를 반환해 기본값을 생성할 수 있도록 합니다.
딕셔너리와의 차이점
딕셔너리에서 키로 조회를 할때는 __getitem__ 메서드가 호출됩니다. 이때 찾는 키-값이 없다면 __missing__ 메서드를 호출합니다.
딕셔너리는 __missing__ 메서드를 오버라이드 하지 않았기 때문에, 키가 없을 때 KeyError를 발생시킵니다.
dict 클래스의 __getitem__ 메서드
def __getitem__(self, key):
value = self._lookup(key).value
if value is None:
# Check if we're a subclass.
if type(self) is not Dict:
# Try to call the __missing__ method.
missing = getattr(self, "__missing__")
if missing is not None:
return missing(key)
raise KeyError("no such key: {0!r}".format(key))
return value
defaultdict 클래스의 __missing__ 메서드
defaultdict는 __missing__
메서드를 오버라이딩하여, dict의 __getitem__에서 missing을 호출할 때 기본 값 생성을 위한 팩토리 함수를 반환합니다.
class defaultdict(dict):
def __init__(self, *args, **kwargs):
if 'default_factory' in kwargs:
self.default_factory = kwargs['default_factory']
del kwargs['default_factory']
else:
self.default_factory = None
super().__init__(self, *args, **kwargs)
def __missing__(self, key):
return self.default_factory
그리고 이러한 기본 팩토리를 저장하기위한 인스턴스 변수 default_factory
를 가지고 있습니다. 이 인스턴스 변수는 __missing__
메서드에서 사용됩니다.
defaultdict 클래스
- default_factory는 키가 없을 때 반환할 데이터 타입을 지정합니다.
- default_factory는 함수나 callable 객체를 지정할 수 있습니다.
- default_factory의 기본값은 None 입니다.
class collections.defaultdict([default_factory[, ...]])
사용 이점
키가 없으면 기본값을 생성하여 반환하기 때문에, 키가 없을 때 미리 데이터를 채우는 코드를 작성할 필요 없습니다.
예시를 통해 비교해보겠습니다. defaultdict을 사용하지 않는경우, 키가 없는 경우를 대비해 리스트로 초기화를 해줘야하는 코드가 필요합니다.
d = {}
for k, v in s:
if k not in d:
d[k] = []
d[k].append(v)
defaultdict을 사용하는 경우 리스트를 팩토리 함수로 전달하여 키가 없을 경우 리스트를 반환합니다.
from collections import defaultdict
d = defaultdict(list)
사용 예시
default_factory를 리스트로 지정
기본 팩토리를 리스트로 지정하는 방법은 다음과 같습니다.
from collections import defaultdict
d = defaultdict(list) # 키가 없으면 빈 리스트를 반환합니다.
리스트를 default_factory로 사용하는 공식문서 예제입니다.
s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
d = defaultdict(list)
for k, v in s:
d[k].append(v)
sorted(d.items())
# [('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]
default_factory를 정수형(int)으로 지정
기본 팩토리를 정수형(int)으로 지정하는 방법은 다음과 같습니다. int 타입을 호출하면 0을 반환합니다.
from collections import defaultdict
d = defaultdict(int) # 키가 없으면 0을 반환합니다.
정수형을 default_factory로 사용하는 공식문서 예제입니다.
s = 'mississippi'
d = defaultdict(int)
for k in s:
d[k] += 1
sorted(d.items())
# [('i', 4), ('m', 1), ('p', 2), ('s', 4)]
마무리
파이썬에서 dict를 사용하면서 키가 없을 때 마주치는 KeyError를 처리할 때 기본값을 직접 설정해주곤 합니다. 이러한 코드는 우리의 코드베이스에서 예외처리 부분에 해당하여 코드의 가독성을 해칠 수 있습니다. 파이썬에서는 이러한 문제를 해결하기 위해 defaultdict라는 dict의 서브 클래스를 제공합니다.
defaultdict를 사용하면 위에서 설명한 예시처럼 키가 없을 때 기본값을 생성하여 반환하기 때문에, 키가 없을 때 미리 데이터를 채우는 코드를 작성할 필요 없습니다. 이러한 장점으로 defaultdict를 사용하면 코드의 가독성이 높아집니다.
이처럼 불필요한 코드 작성 시간과 가독성을 높이기 위해서 defaultdict를 사용할 수 있으니 꼭 알아두시기 바랍니다.