파이썬 defaultdict 사용법

2023년 04월 09일

Python

# 파이썬# Python# collections# dict# defaultdict

📕 목차

파이썬에는 해시 테이블 기반의 키-값 쌍으로 이루어진 딕셔너리(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를 사용할 수 있으니 꼭 알아두시기 바랍니다.

참고자료

profile

박민기

단순하게 살아라. 현대인은 쓸데없는 절차와 일 때문에 얼마나 복잡한 삶을 살아가는가? - 이드리스 샤흐

© 2023, 미나리와 함께 만들었음