Python 딕셔너리 타입 정리

2023년 5월 1일 수정

이 글은 Python의 딕셔너리 타입(Dictionary Type, 사전형)과 관련된 내용을 정리한다. 예제 코드는 3.7 버전에서만 돌려봤지만 아마도 2.x 버전에서도 별 문제 없이 실행 가능할거라 생각된다.

딕셔너리 타입

Python의 딕셔너리 타입(Dictionary Type)은 dict 라는 이름의 내장 클래스로써, 하나 이상의 키를 이용해 각 키의 값을 읽거나 쓸 수 있는 타입이며, 여러 데이터를 담을 수 있어서 일반적으로 콜렉션 타입(Collection Type)의 한 종류로 분류된다.

보통 이런 종류의 자료구조로 사전형이라 부르는 타입과 동일하거나 혹은 해시맵(Hash Map), 해시테이블(Hash Table)등의 자료구조와 유사하다.

딕셔너리 인스턴스 생성

아래 명령으로 빈 딕셔너리를 생성할 수 있다.

>>> d = {}

중괄호는 Python에서 딕셔너리 타입을 정의할 때 주로 사용되기 때문에 중괄호는 곧 딕셔너리라고 인식해도 크게 틀리진 않는다.

이제 특정 키를 이용해 값을 쓸 수 있다.

>>> d['key'] = 'value'
>>> d['other'] = 123
>>> d
{'key': 'value', 'other': 123}

물론 한 번에 바로 생성할 수도 있다.

>>> d = {'key': 'value', 'other': 123}
>>> d
{'key': 'value', 'other': 123}

아예 dict 클래스의 생성자를 활용하는 방법도 있다.

>>> d = dict(key='value', other=123)
>>> d
{'key': 'value', 'other': 123}

매개변수로 튜플 리스트를 사용할 수도 있다.

>>> d = dict([('key', 'value'), ('other', 123)])
>>> d
{'key': 'value', 'other': 123}

이 외에도 다양한 방법이 있을 수 있다.

키 읽기, 쓰기, 수정, 삭제

키의 값을 읽을 때는 배열 원소 참조하는 것과 비슷한 문법을 사용하지만 인덱스 대신 키를 사용한다는 점이 다르다.

>>> print(d['key'])
value

만약 없는 키를 읽으려고 하면 KeyError 예외가 발생한다.

>>> print(d['unknown'])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'unknown'

굳이 예외처리를 하고 싶지 않다면 키가 없을 때의 기본 값을 지정할 수 있는 get() 메서드를 활용해서 값을 읽는 것이 편하다.

>>> print(d.get('key'))
value
>>> print(d.get('unknown', 'no key'))
no key
>>> print(d.get('another unknown'))
None

get() 메서드의 두 번째 인자가 기본값이며 이 값이 지정되지 않은 경우는 None 이 기본값이 된다.

값을 쓰는 것은 제일 처음의 읽는 방법과 비슷하다.

>>> d['a'] = 'aaa'
>>> d
{'key': 'value', 'other': 123, 'a': 'aaa'}

하지만 set() 메서드는 별도로 없는 것 같다.

값을 수정하는 것은 그냥 값을 쓰는 것과 동일하다.

>>> d['a'] = 'aaaaa'
>>> d
{'key': 'value', 'other': 123, 'a': 'aaaaa'}

키를 삭제하려면 평범하게(?) del() 을 쓸 수 있다.

>>> del(d['a'])
>>> d
{'key': 'value', 'other': 123}

딕셔너리끼리 합치기

일반적인 방법으로 update 메서드를 활용할 수 있다.

>>> a = {'a':1, 'aa': 2}
>>> b = {'b':3, 'bb': 4}
>>> a.update(b)
>>> a
{'a': 1, 'aa': 2, 'b': 3, 'bb': 4}

update() 메서드는 원본을 변조하는 파괴적인(destructive) 방법이라 병렬 처리 시 인스턴스의 레퍼런스가 다른 태스크에서도 공유된다면 조심해서 사용해야 할 것 같다.

이 외의 트릭으로 Python 3.5 이상에서는 아래 방법을 쓸 수 있다.

>>> a = {'a': 1, 'aa': 2}
>>> b = {'b': 3, 'bb': 4}
>>> {**a, **b}
{'a': 1, 'aa': 2, 'b': 3, 'bb': 4}

Immutable 작업 스타일로 쓸 수가 있어서 병렬 처리 환경에서도 안심하고 쓸 수 있을 것 같다.

딕셔너리에 다른 딕셔너리를 풀어 넣기

딕셔너리를 생성할 때 다른 딕셔너리의 값들을 같이 가져가려면 ** 를 활용할 수 있다.

>>> a = {'john': 100}
>>> b = {'james': 50, 'kim': 99}
>>> c = {'lee': 98, **a, **b}
>>> c
{'lee': 98, 'john': 100, 'james': 50, 'kim': 99}

이건 사실 위의 합치기 두 번째 예제랑 다를 게 없다. 하지만 목적이 좀 다르게 느껴저서 별도의 항목으로 분리했다.

순회하기(Enumeration)

보통은 아래처럼 모든 키의 리스트(혹은 리스트 제너레이터)를 돌려주는 keys() 메서드를 이용해 모든 키의 값을 읽을 수 있다.

>>> d = {'key': 'value', 'other': 123}
>>> for k in d.keys():
...     print(f'{k} = {d.get(k)}')
...
key = value
other = 123

이 외에 값들만 순회하고자 한다면 values() 메서드를 이용할 수도 있다. 하지만 그다지 쓸 일은 잘 없을 것 같다.

참고

요즘은 왠만해선 딕셔너리나 해시테이블 등의 자료구조에서 키를 문자열 형태로 사용하는 게 일반적이지만, 꼭 문자열만 키로 쓸 수 있는 것은 아니다.

>>> a = {1: 100, 2:200}
>>> a
{1: 100, 2: 200}
>>> a[1]
100

키를 정수로 지정하면 배열과 거의 같은 모습을 구경할 수 있기도 하다.

물론 문자열이 아무래도 활용성이 좋으니 문자열 외의 타입을 키로 사용하는 일은 흔하진 않을 것이다. 아마도…