Python: defaultdict 사용과 __call__ 메소드.

간단하게 생각해서, 그냥 dictionary 보다는 defaultdict 를 사용하는 편이 여러모로 편하다.
RealPython 을 참고하여, 몇가지만 정리하려한다.


defaultdict 는, 키가 없는 dict 를 참조했을 때 KeyError 가 발생함을 방지하려는 목적이 기본이다.
예를 들어, 다음과 같이 기본 dict 로 작업하면 오류가 발생한다.

In [51]: aa = dict()

In [52]: aa
Out[52]: {}

In [53]: aa['name'] = '몰라'

In [54]: aa
Out[54]: {'name': '몰라'}

In [55]: aa['addr']
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-55-ebcb56d4c177> in <module>
----> 1 aa['addr']

KeyError: 'addr'

aa['name'] = '몰라' 처럼 키와 값을 모두 할당하면 문제가 없지만, aa['addr'] 처럼 존재하지 않는 키를 참조했을 경우엔 KeyError 를 맞이해야만 한다.

이걸 방지하고자 defaultdict 를 사용한다.

from collections import defaultdict
In [56]: cc = defaultdict(list)

In [57]: cc['phone']
Out[57]: []

cc = defaultdict(list) 에서 처럼, dict 의 기본값을 list 로 줬기 때문에 cc['phone'] 의 값은 빈리스트로 생성되었다. 일반 dict 라면 당연히 KeyError 가 나야한다.


그런데, 여기서 한가지 궁금증. defaultdict 의 첫번째 인수는 꼭 list, set, str 등이어야만 하나?
물론, 그렇지 않다. Callable 이면 뭐든 가능하다. 즉, 함수도 되고, 클래스도 된다.
따라서 이런 함수를 만들어 사용할 수도 있다. (Effective Python 2nd, p152 참고)

def log_missing2():
    print('Key added')
    return list()

dd = defaultdict(log_missing2)

dd['street']
Key added
[]

dd
defaultdict(<function __main__.log_missing2()>, {'street': []})

기본은 이렇고, 실제로는 저렇게 함수를 사용하는 방법보다는, Class 를 정의하고, __call__ 메소드를 구현하는 방식이 좀 더 보기쉽고, 고급 사용법이라고 할 수 있겠다. (Effective Python 2nd, p155)

class BetterCountMissing:
    def __init__(self):
        self.added = 0

    def __call__(self):
        self.added += 1
        return 0

counter = BetterCountMissing()
result = defaultdict(counter)

이 __call__ 메소드는 어떻게 실행이 되나? 그냥 간단하게 뒤에 괄호를 붙여주면 실행할 수 있다.

counter()
0

마지막으로 한가지만 더.

aa = defaultdict(list) 식으로, defaultdict 안에 넣을 때는 list 라고 했지만, 함수를 정의했을 때는 return list() 식으로 괄호를 붙였다.
왜 그랬을까?

defaultdict(list) 는, Callable 객체를 직접 호출하는 형식이므로 괄호를 넣으면 안된다. 반면, 함수에서 return 할 때는, 실제 객체를 생성해서 넘겨야 하므로 괄호를 붙여서 결과값을 만들어줘야 한다.

….

애매한 점이 조금은 해결되었으려나..
아울러, 오래도록 기억할 수 있으려나?

Author: 아무도안

안녕하세요. 글 남겨주셔서 고맙습니다.