본문 바로가기

3. 알고리즘 공부

[355] 파이썬 딕셔너리(dictionary) 자료형

이미지 출처: 점프투파이썬

1. 딕셔너리 자료형과 해시 테이블

파이썬의 키/값 구조로 이루어진 자료형입니다.

내부적으로 해시 테이블로 구현되어 있습니다.

리스트 자료형은 인덱스(숫자)만을 사용하여 값을 찾을 수 있지만, 딕셔너리는 해시할 수 있는 모든 객체를 키로 사용할 수 있습니다.

불변 객체는 해시할 수 있으며(hashable), 따라서 문자, 숫자, 집합까지 모두 키로 사용 가능합니다.

해시 테이블의 장점은 조회가 \(O(1)\)에 가능하다는 점입니다.

따라서 딕셔너리의 주요 연산 시간 복잡도는 \(O(1)\)에 처리가 가능하다는 장점이 있습니다.

연산 시간 복잡도 결과
len(Dict) \(O(1)\) 요소의 개수 반환
Dict[key] 또는 Dict.get(key) \(O(1)\) 키에 해당하는 값 반환
Dict[key] = value \(O(1)\) 해당 키에 값을 삽입
del Dict[key] 또는 Dict.pop(key) \(O(1)\) 해당 키 삭제(반환 차이)
Dict.clear() \(O(1)\) 모든 키 삭제(초기화)
key in Dict \(O(1)\) 해당 키 존재 여부 반환

심지어 파이썬 3.6부터는 딕셔너리의 메모리 사용량을 20% 정도 줄이는 성능 개선이 이루어졌습니다.

2. 딕셔너리와 관련된 모듈

가. defaultdict

딕셔너리 자료형에서 없는 키를 조회하면 KeyError가 발생합니다.

a = {}

a["key"]
# KeyError: 'key'

KeyError를 방지하기 위해서 try, except문을 사용하거나, get 메서드를 사용할 수 있습니다.

print(a.get("key"))
# None

try:
    print(a["key"])
except:
    print(None)
# None

하지만 defaultdict를 사용하면 편합니다.

defaultdict는 default로 사용할 타입을 지정해야하고, 키가 없을 경우 해당 타입의 기본값을 반환합니다.

from collections import defaultdict

a = defaultdict(str)
a["key"]
# ''

a = defaultdict(int)
a["key"]
# 0

a = defaultdict(list)
a["key"]
# []

a = defaultdict(dict)
a["key"]
# {}

알고리즘 문제를 풀 때, 특정 값의 개수나 횟수를 구할 때 자주 사용합니다.

나. Counter

Counter 객체는 요소의 개수를 계산해 딕셔너리를 상속 받은 객체로 반환합니다.

from collections import Counter

a = [1, 1, 1, 2, 2, 3, 3, 3, 3]
Counter(a)
# Counter({3: 4, 1: 3, 2: 2})

자주 사용하는 메서드는 most_common이 있고, 빈도 수가 높은 순서로 키, 값 쌍을 리스트로 반환합니다.

메서드에 숫자를 입력하면 그 숫자 개수만큼 반환해줍니다.

Counter([1, 1, 1, 2, 2, 3, 3, 3, 3]).most_common(2)
# [(3, 4), (1, 3)]

그리고 Counter 객체끼리는 더하거나 빼는 산술 연산이 가능합니다.

빼서 음수가 되는 경우 해당 키가 사라집니다.

Counter([1, 1, 1, 1, 2, 2, 3, 3]) + Counter([1, 2, 2, 3, 3, 3])
# Counter({1: 5, 3: 5, 2: 4})

Counter([1, 1, 1, 1, 2, 2, 3, 3]) - Counter([1, 2, 2, 3, 3, 3])
# Counter({1: 3})

다. OrderedDict

해시 테이블을 이용한 대부분의 자료형은 입력 순서가 유지되지 않습니다.

하지만 파이썬 3.7부터는 딕셔너리에 입력 순서가 유지되도록 개선되었습니다.

OrderedDict는 그 이전에 사용하던 객체로서 현재는 사실상 하위 호환성을 위해서 남아있습니다.

from collections import OrderedDict

od = OrderedDict({"a": 1, "c": 3, "b": 2})
od
# OrderedDict([('a', 1), ('c', 3), ('b', 2)])
list(od)
# ['a', 'c', 'b']

d = {"a": 1, "c": 3, "b": 2}
d
# {'a': 1, 'c': 3, 'b': 2}
list(d)
# ['a', 'c', 'b']

 

그러나 알고리즘 문제의 채점에 사용되는 파이썬이 하위 버전일 수도 있고, 해시 테이블이라는 자료구조가 원래 순서가 유지되지 않는 만큼 딕셔너리의 입력 순서를 너무 무의식적으로 사용하는 것은 유의해야합니다.