[python] defaultdict


defaultdict


collections.defaultdict([default_factory[, …]])

dict 클래스의 subclass로 dict와 거의 같습니다.

다만 이름에서도 알 수 있듯이,

defaultdict는 기본값이 존재하는 dict입니다.

그 기본값은 default_factory를 무엇으로 지정하느냐에 따라 달라집니다.


예를 들어 살펴보겠습니다.

다음과 같은 코드를 실행한다고 하면

from collections import defaultdict

# default_factory를 입력안했을 때
dd1 = defaultdict()
print('dd1 = ', dd1)
print('dd1[key1] = ', dd1['key1'])

# 출력값
# dd1 =  defaultdict(None, {})
# KeyError: 'key1'

dd1의 default_factory는 아무것도 지정하지 않았기 때문에 None이 됩니다.

그런데 갑자기 정의한 적이 없는 dd1[‘key1’]을 출력하라고 하니 오류가 납니다.


이번에는 이 코드를 실행해 봅니다.

a = int()
print('a = ', a)

dd2 = defaultdict(int)
print('dd2 = ', dd2)
print('dd2[key1] = ', dd2['key1'])

# 출력값
# a =  0
# dd2 =  defaultdict(<class 'int'>, {})
# dd2[key1] =  0

먼저 a 출력값을 통해 int() 클래스의 기본값은 0임을 알 수 있습니다.

그리고 이번에는 dd2에 default_factory를 int로 설정했습니다.

그랬더니 따로 dd2[‘key1’]을 정의해주지 않아도,

자동으로 기본값 0을 가진 key1값이 생성됩니다.


이는 숫자를 0부터 카운트하는데 유용합니다.

다음 코드는 ‘Teemo’안에 들어간 알파벳별 갯수를 구하고 있습니다.

words = 'Teemo'
dd5 = defaultdict(int)
for alphabet in words:
    dd5[alphabet] += 1

print(list(dd5.items()))

# 출력값
# [('o', 1), ('e', 2), ('T', 1), ('m', 1)]

처음 생성될 떄, 기본값이 0으로 설정되니

만약 e를 살펴본다면, 처음에 dd5[‘e’]가 생성될 때 0이었는데 += 1로 인해 1이 됩니다.

그리고 두번째 e는 기존에 dd5[‘e’] = 1 값이 있으므로,

새로 생성되는게 아니라 기존값에 1만 더해서 dd5[‘e’] = 2 가 됩니다.


이번엔 default_factory값을 list로 설정하여,

어떤 메뉴를 누가 골랐는지 요약해 보도록 하겠습니다.

b = list()
print('b = ', b)

dd3 = defaultdict(list)
dd3['key1'].append(2)
print('dd3[key1] = ', dd3['key1'])

# (원하는 메뉴, 이름)
menu_select = [('비빔밥', '티모'), ('돈가스', '징크스'), ('메밀소바', '렝가'), ('비빔밥', '아무무'), ('돈가스', '초가스')]
dd4 = defaultdict(list)
for menu, name in menu_select:
    dd4[menu].append(name)

print('dd4 = ', dd4)
print('dd4.items() = ', dd4.items())
print('menu sum = ', list(dd4.items()))

# 출력값
# b =  []
# dd3[key1] =  [2]
# dd4 =  defaultdict(<class 'list'>, {'돈가스': ['징크스', '초가스'], '비빔밥': ['티모', '아무무'], '메밀소바': ['렝가']})
# dd4.items() =  dict_items([('돈가스', ['징크스', '초가스']), ('비빔밥', ['티모', '아무무']), ('메밀소바', ['렝가'])])
# menu sum =  [('돈가스', ['징크스', '초가스']), ('비빔밥', ['티모', '아무무']), ('메밀소바', ['렝가'])]

먼저 b출력을 통해 list() 클래스의 기본값이 [] 라는걸 알았습니다.

즉, dd3[‘key1’].append(2)는 [].append(2) 와 같겠죠?


default_factory를 set()으로 설정한 하면 위의 과정이 더 간소화됩니다.

# (원하는 메뉴, 이름)
menu_select = [('비빔밥', '티모'), ('돈가스', '징크스'), ('메밀소바', '렝가'), ('비빔밥', '아무무'), ('돈가스', '초가스')]

dd7 = defaultdict(set)
for menu, name in menu_select:
    dd7[menu].add(name)

print('dd7.items() = ', dd7.items())

# 출력값
# dd7.items() =  dict_items([('돈가스', {'징크스', '초가스'}), ('비빔밥', {'티모', '아무무'}), ('메밀소바', {'렝가'})])


default값을 custom하게 정할 수도 있습니다.

다음과 같이 lambda 함수를 사용하면 됩니다.

# custom 기본값
dd8 = defaultdict(lambda: 'asdf')
print('dd8["key1"] = ', dd8['key1'])

dd6 = defaultdict(lambda: 'teemo')
print('dd6 = ', dd6)
print('dd6["object"] = ', dd6['object'])
dd6.update(action='throws', what='mushroom', where='bush')
print('{asdf} {action} {what} into {where}'.format(**dd6))

# 출력값
# dd8["key1"] =  asdf
# dd6 =  defaultdict(<function <lambda> at 0x10205f840>, {})
# dd6["object"] =  teemo
# teemo throws mushroom into bush

key값을 무엇으로 하든 처음 생성될 때는,

lambda에서 정해준 값이 기본값이 되는 걸 볼 수 있습니다.