본문 바로가기

파이썬 기초

collections.defaultdict와 일반적인 파이썬 내장 자료구조인 사전(Dictionary)을 사용하는 예

def countLetters(word):
    counter = {}
    for letter in word:
        if letter not in counter:
            counter[letter] = 0
        counter[letter] += 1
    return counter

print(countLetters('hannibal'))

 

출력결과

 

 

파이썬의 내장 자료구조인 사전(dictionary)를 사용하다 보면 어떤 키(key)에 대한 값(value)이 없는 경우에 대한 처리를 해야하는 경우가 자주 발생합니다. 이번 포스팅에서는 이러한 경우 일반적으로 어떻게 처리를 하는지 살펴보고, 관련해서 파이썬에서 제공하는 몇가지 방법을 알아보도록 하겠습니다.

 

일반적인 사전 기본값 처리

아래 코드는 주어진 단어에 들어있는 각 알파벳 글자의 수를 세어서 사전에 저장해주는 함수입니다.

def countLetters(word):
    counter = {}
    for letter in word:
        if letter not in counter:
            counter[letter] = 0
        counter[letter] += 1
    return counter
 

for 루프 안에 if 조건절을 통해서 counter 사전에 어떤 글자가 키(key)로 존재하지 않는 경우, 해당 키에 대한 기본값을 0으로 세팅해주고 있는데요. 이러한 코딩 패턴은 파이썬에서 사전을 사용할 때 상당히 자주 접할 수 있는데, 코드 가독성 측면에서는 이렇게 사소한 처리가 주요 흐름을 파악을 하는데 방해가 되기도 합니다. 그리고 collections.defaultdict의 탄생은 바로 이러한 불편함에서 시작한다!!!

 

나은 방법: dict.setdefault

위와 같은 if 조건절을 피할 수 있도록 파이썬의 사전(dictionary) 자료구조는 setdefault 함수를 제공합니다. 첫번째 인자로 키(key)값, 두번째 인자로 기본값(default value)를 넘기면 되는데요.

def countLetters(word):
    counter = {}
    for letter in word:
        counter.setdefault(letter, 0)
        counter[letter] += 1
    return counter
 

다만 코드가 깔끔해져서 좋기는데, for 루프 내에서 setdefault 함수가 무조건적으로 항상 호출되는 부분이 좀 마음에 들지 않습니다.

 

더 나은 방법: collections.defaultdict

파이썬의 내장 모듈인 collections의 defaultdict 클래스는 이러한 경우 사용하면 딱 인데요. defaultdict 클래스의 생성자로 기본값을 생성해주는 함수를 넘기면, 모든 키에 대해서 값이 없는 경우 자동으로 생성자의 인자로 넘어온 함수를 호출하여 그 결과값으로 설정해줍니다.

먼저, collections 모듈의 defaultdict 클래스는 다음과 같이 임포트해야 합니다.

from collections import defaultdict

이제, 위에서 작성한 코드를 임포트한 defaultdict를 이용해서 개선하면, for 루프로 부터 사전의 기본값 처리 코드를 완전히 제거할 수가 있습니다.

from collections import defaultdict

def countLetters(word):
    counter = defaultdict(int)
    for letter in word:
        counter[letter] += 1
    return counter

(즉 위에서 counter는 { } 를 의미하는 Dictionary인것!!! 그리고 어떠한 키값이 오더라도 그에 해당하는 값이 없는 경우 자동으로 생성자의 인자로 넘어온 함수를 호출하여 그 결과값으로 설정해 주는 것!)

 

여기서 defaultdict 클래스의 생성자로 int 함수를 넘긴 이유는 int()는 0을 리턴하기 때문입니다. 람다 함수를 활용해서 다음과 같이 int 함수 대신에 lambda: 0를 넘겨도 동일하게 작동을 합니다.

 

from collections import defaultdict

def countLetters(word):
    counter = defaultdict(lambda: 0)
    for letter in word:
        counter[letter] += 1
    return counter
#1
def countLetters(word):
    counter=defaultdict(int)
    for letter in word:
        counter[letter]+=1
    return counter

print(countLetters('Hannibal'))

#2
def groupWords(words):
    grouper=defaultdict(list)
    for word in words:
        length=len(word)
        grouper[length].append(word)
    return grouper


words=['kakao','one','222','333','o']
print(groupWords(words))

# 1과2의 비교: defaultdict클래스의 생서자로 int가 오는지 아니면 list가 오는지(이에 따라 
# Dictionary의 value의 형식이 결정됨. int가 오면 value가 int형식이 되고 list가 오면 value가 list형식을 지님)
# 그리고 맵의 키값으로 문자인지 아니면 각단어의 길이가 오는지.

 

사전 기본값으로 빈 리스트 세팅하기

collections.defaultdict를 활용할 수 있는 다른 사례로 데이터를 특정 기준에 의해 카테고리로 묶는 경우를 들 수 있습니다.

예를 들어, 주어진 단어들을 길이에 따라 분류해주는 코드는 다음과 같이 작성할 수 있습니다.

from collections import defaultdict

def groupWords(words):
    grouper = defaultdict(list)
    for word in words:
        length = len(word)
        grouper[length].append(word)
    return grouper

이번에는 defaultdict 생성자에 list 함수를 넘겼기 때문에, grouper 사전에 어떤 글자가 키(key)로 존재하지 않는 경우(길이가 5인 단어가 처음으로 등장해 5라는 키가 존재하지 않는 경우를 말함), 해당 키에 대한 기본값을 비어있는 리스트(empty list)로 세팅해줍니다.

만약에 collections.defaultdict 클래스 없이 위 코드를 작성해야했다면 다음과 같이 다소 지저분하게 작성했었을 것입니다.

def groupWords(words):
    grouper = {}
    for word in words:
        length = len(word)
        if length not in grouper:
            grouper[length] = []
        grouper[length].append(word)
    return grouper

 

[보너스] 사전 기본값으로 빈 세트(set) 세팅하기

한 번 응용을 해볼까요? 위에서 작성한 코드에서 단어들을 길이에 따라 분류할 때 중복되지 않은 단어만 필요하다면 어떻게 해야할까요? defaultdict 생성자에 list 함수 대신에 set 함수를 넘기고, append 함수 대신에 add 함수를 이용해서 단어를 넘기면 됩니다. :)

from collections import defaultdict

def groupWords(words):
    grouper = defaultdict(set)
    for word in words:
        length = len(word)
        grouper[length].add(word)
    return grouper

 

마치면서

이상으로 파이썬에서 제공하는 dict.setdefault 함수와 collections.defaultdict 클래스를 이용해서 사전에 기본값을 세팅하는 방법에 대해서 알아보았습니다. 본 포스팅에서 소개한 요령들을 잘 활용해셔서 사전을 사용하실 때 좀 더 읽기쉬운 코드를 작성하실 수 있으셨으면 좋겠습니다.

 

 

출처: https://www.daleseo.com/python-collections-defaultdict/