# 딕셔너리 리터럴
= {"a": 1, "b": 2, "c": 3}
my_dict my_dict
{'a': 1, 'b': 2, 'c': 3}
dictionary
)파이썬 딕셔너리(dict
)는 키: 값
쌍으로 구성된 매핑(mapping) 자료형이다.
키는 “이름”이라고 생각하면 되는데, 어떤 이름과 여기 대응하는 값을 매칭(매핑)시켜 필요할 때 사용한다.
리스트를 비롯한 파이썬 시퀀스(sequence
)는 인덱스(index)를 통해서 값에 접근하는 반면 딕셔너리는 키를 통해서 값에 접근한다는 점이 다르다.
딕셔너리는 iterable
에 속한다.
딕셔너리는 값을 바꿀 수 있는 mutable
객체이다.
클래스와 인스턴스의 attributes, 모듈 네임스페이스, 함수의 키워드 인자 등은 모두 딕셔너리로 구현된다. 따라서 이들의 동작을 이해하기 위해선 딕셔너리를 이해하는 것이 중요하다.
딕셔너리를 만드는 방법은 여러 가지가 있다. 먼저 딕셔너리 리터럴(literal)을 이용하는 것이다.
# 딕셔너리 리터럴
= {"a": 1, "b": 2, "c": 3}
my_dict my_dict
{'a': 1, 'b': 2, 'c': 3}
리터널로 빈 딕셔너리를 만들고, 여기에 키와 값을 추가하는 방법도 있다.
# 빈 딕셔너리를 만들고 나서 채우기
= {}
my_dict 'a'] = 1
my_dict['b'] = 2
my_dict['c'] = 3
my_dict[ my_dict
{'a': 1, 'b': 2, 'c': 3}
딕셔너리를 만드는 또 다른 방법은 dict()
생성자를 사용하는 것이다.
# dict 생성자
= dict(a=1, b=2, c=3)
my_dict my_dict
{'a': 1, 'b': 2, 'c': 3}
튜플로 구성된 iterable
을 재료로 사용하여 딕셔너리를 만들 수 있다. 이것을 바로 dict()
생성자의 인자로 넘겨서 만들거나, dict comprehension
에서 풀어서 만들 수 있다.
# a list of tuples로부터 만들기
= [('b', 1), ('c', 2), ('a', 3)]
source = dict(source)
my_dict2 my_dict2
{'b': 1, 'c': 2, 'a': 3}
# dict 컴프리헨션
= [('a', 1), ('b', 2), ('c', 3)]
source = {k: v for k, v in source}
my_dict3 my_dict3
{'a': 1, 'b': 2, 'c': 3}
키를 이용하여 값에 접근할 때 d[key]
문법을 사용한다.
= {"a": 1, "b": 2, "c": 3}
my_dict 'a'] my_dict[
1
딕셔너리에 없는 키를 사용하면 KeyError
가 발생한다. 이와 같은 경우를 피하기 위해서 d.get('key')
메서드를 사용하거나, d.setdefault('key', default_value)
메서드를 사용할 수 있는데, 이것은 뒤에서 자세히 설명한다.
= {"a": 1, "b": 2, "c": 3}
my_dict "d"] my_dict[
Membership 체크: 어떤 키가 해당 딕셔니리에 존재하는지 확인할 때는 k in d
operator를 사용한다.
= {"a": 1, "b": 2, "c": 3}
my_dict "d" in my_dict
False
키: 값
쌍의 개수: len()
함수를 사용한다.
= {"a": 1, "b": 2, "c": 3}
my_dict len(my_dict)
3
d.keys()
메서드는 딕셔너리 키, d.values()
메서드는 딕셔너리의 값들, d.items()
메서드는 키, 값
쌍과 연관된 iterable
을 반환한다. 아래 코드 결과에서 보듯이 좀 특수한 객체로 인덱스를 사용하여 접근할 수는 없다. 대신에 for
문을 사용하여 순회할 수 있다.
이들은 모두 view
객체로, 딕셔너리의 변경 사항을 반영한다. 이들은 딕셔너리 뷰(dictionary view
)라고 불리며, 셋(set)과 비슷한 동작을 한다. 다음 절에서 자세히 설명한다.
= {"a": 1, "b": 2, "c": 3}
my_dict my_dict.keys()
dict_keys(['a', 'b', 'c'])
my_dict.values()
dict_values([1, 2, 3])
my_dict.items()
dict_items([('a', 1), ('b', 2), ('c', 3)])
딕셔너리 키는 d.keys()
, 값은 d.values()
, 키, 값
쌍은 d.items()
를 이용하여 접근할 수 있다. 이들은 딕셔너리 뷰(dictionary view
)라고 한다. 뷰라고 하는 것은 이 메서드가 반환한 객체가 원래의 딕셔너리와 연결되어 있다는 것을 의미한다. 따라서 원래의 딕셔너리의 내용이 바뀌면 이 뷰의 내용도 바뀐다.
반환되는 값들은 iterable
이기 때문에 for
문을 사용하여 순회할 수 있으며, 또한 셋(set)과 비슷한 동작을 한다.
다음과 같은 딕셔너리가 있다고 하자.
= [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6)]
source = {k: v for k, v in source} my_dict
= my_dict.keys()
my_keys my_keys
dict_keys(['a', 'b', 'c', 'd', 'e', 'f'])
이 객체에 대하여 인덱스를 사용하면 아래에서 보는 바와 같이 에러가 발생한다.
0] my_keys[
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[14], line 1 ----> 1 my_keys[0] TypeError: 'dict_keys' object is not subscriptable
다음은 값에 대해 접근하는 방법이다.
my_dict.values()
dict_values([1, 2, 3, 4, 5, 6])
딕셔너리의 키, 값
쌍에 대해 접근하는 방법은 다음과 같다.
my_dict.items()
dict_items([('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6)])
딕셔너리 뷰는 iterable
이기 때문에 for
문을 사용하여 순회할 수 있다.
for k in my_dict.keys():
print(k, my_dict[k])
a 1
b 2
c 3
d 4
e 5
f 6
for v in my_dict.values():
print(v)
1
2
3
4
5
6
for k, v in my_dict.items():
print(k, v)
a 1
b 2
c 3
d 4
e 5
f 6
딕셔너리 뷰는 일종의 셋(set, 집합)으로 집합 연산자를 제공한다. 파이썬 셋(set)은 다음 장에서 다룬다.
|
: 합집합&
: 교집합-
: 차집합^
: Symmetric difference= [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6)]
source = {k: v for k, v in source}
my_dict = {"e": 55, "f": 66, "g": 77}
other_dict # |, &, -. ^을 바꿔보자.
| other_dict.keys() my_dict.keys()
{'a', 'b', 'c', 'd', 'e', 'f', 'g'}
& other_dict.keys() my_dict.keys()
{'e', 'f'}
- other_dict.keys() my_dict.keys()
{'a', 'b', 'c', 'd'}
^ other_dict.keys() my_dict.keys()
{'a', 'b', 'c', 'd', 'g'}
딕셔너리의 키(key)로 사용할 수 있는 것은 hashable
이라야 한다. 이는 딕셔너리의 핵심 기능이 어떤 이름을 가지고 그 이름에 해당하는 값을 찾는 것이고, 이를 위해서 내부적으로 hash table
이라는 엔진을 사용하기 때문이다. hashable
이라고 하는 것은 프로그래밍 공간에 유일하게 존재하여, 고유한 hash code
를 가지고 있어야 함을 의미하고, 그렇기 때문에 immutable
특성을 가지고 있어야 한다. 따라서 다음 값들일 딕셔너리의 키가 될 수 있다.
str
: 가장 많이 사용따라서 list
는 mutable
객체이기 때문에 딕셔너리 키가 될 수 없다.
다음과 같은 딕셔너리가 있다.
= [('a', 1), ('b', 2), ('c', 3)]
source = {k: v for k, v in source}
my_dict my_dict
{'a': 1, 'b': 2, 'c': 3}
d[key]
를 사용하여 키에 해당하는 값을 가져올 수 있다. 만약 키가 존재하지 않으면 KeyError
가 발생한다.
'a'] my_dict[
1
d.get('key')
를 사용하여 키에 해당하는 값을 가져올 수 있다. 만약 키가 존재하지 않으면 None
을 반환한다.
'd') my_dict.get(
d.get('key', default_value)
와 같이 인자 두 개을 사용하는 경우는 키가 딕셔너리에 있으면 해당 키의 값을 반환하고, 키가 없으면 default_value
를 반환한다.
'd', 4) my_dict.get(
4
어떤 키의 값을 바꾼다.
'c'] = 33
my_dict[ my_dict
{'a': 1, 'b': 2, 'c': 33}
새로운 키, 값을 추가한다.
'd'] = 44
my_dict[ my_dict
{'a': 1, 'b': 2, 'c': 33, 'd': 44}
update()
메서드에 (키, 값)
으로 구성된 iterable
을 전달하여 업데이트 할 수 있다.
'e', 5), ('f', 6)])
my_dict.update([( my_dict
{'a': 1, 'b': 2, 'c': 33, 'd': 44, 'e': 5, 'f': 6}
update()
메서드에 딕셔너리를 전달하여 업데이트 할 수 있다.
"g": 7, "h": 8})
my_dict.update({ my_dict
{'a': 1, 'b': 2, 'c': 33, 'd': 44, 'e': 5, 'f': 6, 'g': 7, 'h': 8}
del
키워드를 사용하여 키, 값을 삭제할 수 있다.
del my_dict['a']
my_dict
{'b': 2, 'c': 33, 'd': 44, 'e': 5, 'f': 6, 'g': 7, 'h': 8}
pop()
메서드를 사용하여 키, 값을 삭제하고, 해당 키의 값을 반환한다. 만약 키가 존재하지 않으면 KeyError
가 발생한다.
'b')
my_dict.pop( my_dict
{'c': 33, 'd': 44, 'e': 5, 'f': 6, 'g': 7, 'h': 8}
popitem()
메서드를 사용하여 마지막에 추가된 키, 값
쌍을 삭제하고, 해당 키, 값을 튜플로 반환한다.
my_dict.popitem() my_dict
{'c': 33, 'd': 44, 'e': 5, 'f': 6, 'g': 7}
clear()
메서드를 사용하여 딕셔너리의 모든 키, 값을 삭제한다.
my_dict.clear() my_dict
{}
copy()
메서드를 사용하여 딕셔너리를 복사한다.
= {"a": 1, "b": 2, "c": 3}
my_dict = my_dict.copy()
my_dict2 my_dict2
{'a': 1, 'b': 2, 'c': 3}
d1 == d2
를 사용하여 두 딕셔너리가 같은지 비교할 수 있다. 두 딕셔너리의 키와 값이 모두 같으면 True
를 반환한다.
= {"a": 1, "b": 2, "c": 3}
my_dict1 = {"a": 1, "b": 2, "c": 3}
my_dict2 == my_dict2 my_dict1
True
d1 != d2
를 사용하여 두 딕셔너리가 다른지 비교할 수 있다. 두 딕셔너리의 키와 값이 모두 다르면 True
를 반환한다.
= {"a": 1, "b": 2, "c": 3}
my_dict1 = {"a": 1, "b": 2, "c": 4}
my_dict2 != my_dict2 my_dict1
True
|
을 사용하여 두 딕셔너리를 합칠 수 있다(merging). 키가 같은 경우에는 기존의 키 값을 바꾼다.
= {"e": 55, "f": 66, "g": 77}
other_dict print(my_dict)
| other_dict my_dict
{'a': 1, 'b': 2, 'c': 3}
{'a': 1, 'b': 2, 'c': 3, 'e': 55, 'f': 66, 'g': 77}
|=
는 왼쪽의 딕셔너리를 업데이트 한다.
= {"e": 55, "f": 66, "g": 77}
other_dict |= other_dict
my_dict my_dict
{'a': 1, 'b': 2, 'c': 3, 'e': 55, 'f': 66, 'g': 77}
딕셔너리 컴프리헨션(dict comprehension
)은 리스트 컴프리헨션과 비슷하게 딕셔너리를 만드는 방법이다. 문법은 다음과 같다. 리스트 컴프리헨션과 비슷하게 for
문과 if
문을 사용할 수 있는데, 전체를 {}
로 감싸서 딕셔너리로 만든다.
for k, v in iterable if condition} {key_expr: value_expr
다음은 dict comprehension
을 사용하는 예시이다.
= {
prices 'ACME': 45.23,
'AAPL': 612.78,
'IBM': 205.55,
'HPQ': 37.20,
'FB': 10.75
}
위와 같은 dict에서 주가가 200
이상인 것들로 구성된 딕셔너리를 구성해 보자.
for k, v in prices.items() if v > 200} {k: v
{'AAPL': 612.78, 'IBM': 205.55}
테크 기업들이 tech_names = { 'AAPL', 'IBM', 'HPQ', 'MSFT' }
로 주어졌을 때, 이 회사들에 대한 정보만 추출해 보자.
= { 'AAPL', 'IBM', 'HPQ', 'MSFT' }
tech_names for k, v in prices.items() if k in tech_names} {k: v
{'AAPL': 612.78, 'IBM': 205.55, 'HPQ': 37.2}
딕셔너리 언팩킹은 딕셔너리의 키, 값을 풀어서 다른 딕셔너리에 추가하는 방법이다. 딕셔너리 언팩킹은 **
연산자를 사용하여 딕셔너리를 다른 딕셔너리에 추가할 때 사용한다.
= {"a": 1, "b": 2}
my_dict = {"c": 3, "d": 4}
other_dict = {**my_dict, **other_dict}
my_dict my_dict
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
딕셔너리 언팩킹은 딕셔너리를 함수를 호출할 때 키워드 인자로 전달할 때도 사용된다. 이때는 **
연산자를 사용하여 딕셔너리의 키, 값을 함수의 키워드 인자로 전달한다.
def my_func(a, b, c):
return a + b + c
= {"a": 1, "b": 2, "c": 3}
my_dict **my_dict) my_func(
6
키-값 쌍의 관계는 프로그램에서 자주 사용되는 패턴이다. 그래서 프로그래밍을 할 때 딕셔너리에 다이내믹 데이터를 추가하거나 업데이트 하는 경우가 많다. 이런 과정에서 거의 항상 키가 존재하는지 확인해야 하는 경우가 있다. 보통 키가 존재하면 해당 키에 대합 값을 업데이트하고, 키가 존재하지 않으면 새로 값을 추가하는 로직을 많이 사용하게 된다.
다음 사례를 보자. 문자열 시퀀스 'abracadabra'
에 있는 각 알파벳의 개수를 구하여, 그 결과를 다음과 같은 딕셔너리로 만들고자 한다.
{ "a": 5,
"b": 2,
"c": 1,
"d": 1,
"r": 2
}
이제 시작해 보자. 먼저 빈 딕셔너리를 만들고, 문자열을 순회하면서 각 알파벳의 개수를 세는 코드를 작성해 보자.
= 'abracadabra'
my_str # 결과를 담을 딕셔너리
= {} my_dict
문자열을 시퀀스(sequence
)의 한 종류이기 때문에 for
문을 사용하여 순회할 수 있다. 그래서 문자 하나하나에 대해 결과를 담을 my_dict
딕셔너리에 해당 문자의 개수를 업데이트 하는 코드를 작성할 수 있다. 이 경우 in
연산자를 사용하여 해당 문자가 딕셔너리에 있는지 확인하고, 있으면 값을 업데이트하고, 없으면 새로 추가하는 코드를 작성할 수 있다.
for char in my_str:
if char in my_dict:
+= 1
my_dict[char] else:
= 1
my_dict[char] my_dict
{'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1}
d.get()
메서드를 사용하면 if/else
문을 사용하지 않고도 위의 코드를 작성할 수 있다. 이 함수는 d.get('key')
은 key
가 있을 때는 d['key']
값을 반환하고, 해당 키에 대한 값이 없을 때는 None
을 반환한다. 그런데 d.get('key', default_value)
형식으로 2개 인자로 사용하면 'key'
에 해당하는 값이 없으면 그 default_value
값을 반환한다.
위의 코드를 d.get()
함수를 사용하면 다음과 같이 만들 수 있다. 어떤 문자가 처음 처리되는 경우는 my_dict.get(char, 0)
코드는 0
이 될 것이고, 여기에 1
을 더한 결과가 이 문자가 키가 되는 값이 된다. 어떤 문자가 다시 처리되는 경우는 my_dict.get(char, 0)
가 기존의 값이 될 것이고, 여기에 1
이 더해져 그 값이 업데이트된다.
= {}
my_dict = 'abracadabra'
my_str for char in my_str:
# char가 딕셔너리에 없으면 0을 반환하고, 있으면 해당 값을 반환
= my_dict.get(char, 0)
count = count + 1
my_dict[char] my_dict
{'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1}
다음은 d.setdefault()
메서드를 이용하는 경우이다. 이 함수는 그 이름에 논란이 많다. 왜냐하면 값을 가지고 오는 기능을 하는데도 set
이라는 단어가 들어가기 때문이다. 물론 set
이 의미도 가지고 있다. 디폴트 값을 설정하는 기능을 한다.
d.setdefault('key', default_val)
형태로 사용하는 데, 'key'
가 존재하면 해당 키의 값이 반환되고, 'key'
가 존재하지 않는 경우에는 default_val
이 해당 키의 값으로 셋팅된다.
= {"a": 3, "b": 1, "c": 2}
ex_dict1 'c', 4)
ex_dict1.setdefault( ex_dict1
{'a': 3, 'b': 1, 'c': 2}
= {"a": 3, "b": 1, "c": 2}
ex_dict2 'd', 4)
ex_dict2.setdefault( ex_dict2
{'a': 3, 'b': 1, 'c': 2, 'd': 4}
이와 같은 d.setdefault()
를 사용하여 위 문제를 풀어보면 다음과 같이 할 수 있다.
= {}
my_dict = 'abracadabra'
my_str for char in my_str:
= my_dict.setdefault(char, 0)
count = count + 1
my_dict[char] my_dict
{'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1}
위와 같이 딕셔너리의 값을 업데이트 해야 하는 문제에서는 어떤 키가 아직은 존재하지 않을 때 이를 처리하는 문제가 중요하게 대두된다. 이와 같은 문제가 흔하기 때문에 collections
의 defaultdict()
이 이 문제를 쉽게 풀 수 있는 방법을 제공한다.
이 메서드는 소위 default_factory
라는 인자를 받는데, 이것은 초기값을 만드는 데 사용되는 파이썬 함수이다. 아래 코드에서는 int
가 그것이다. 이 메서드로 생성되는 딕셔너리는 없는 키에 대한 값을 요구받으면 해당 키에 대하여 해당 함수를 통해 빈 초깃값을 만든다. 만약 리스트에 값을 담을 것이면 list
함수를 주면 된다.
from collections import defaultdict
= defaultdict(int)
my_dict = 'abracadabra'
my_str for char in my_str:
+= 1
my_dict[char]
dict(my_dict)
{'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1}
정리하면 키가 없는 경우를 처리하는 방법은 다음과 같다.
if/else
문에서 in
연산자를 사용하여 키가 있는지 확인하고, 있으면 값을 업데이트하고, 없으면 새로 추가한다.d.get('key', default_value)
메서드를 사용하여 키가 있는지 확인하고, 있으면 해당 값을 가져오고, 없으면 default_value
를 반환한다.d.setdefault('key', default_value)
메서드를 사용하여 키가 있는지 확인하고, 없으면 default_value
를 해당 키의 값으로 설정한다.collections.defaultdict()
를 사용하여 키가 없는 경우에 대한 기본값을 설정한다. 이 메서드는 default_factory
라는 인자를 받아서, 해당 키가 없을 때 사용할 기본값을 만드는 함수를 지정한다.in
식을 사용하는 방법, KeyError
예외를 사용하는 방법, get()
메서드를 사용하는 방법, setdefault()
메서드를 사용하는 방버이 있다.get
메서드가 가장 좋고, 딕셔너리에 넣을 값을 만드는 비용이 비싸거나 만드는 과정이세 예외가 발생할 수 있는 경우에도 get
메서드를 사용하는 편이 좋다.dict
의 setdefault()
메서드를 사용하는 방법이 가장 적합해 보인다면 setdefault
대신 defaultdict
를 사용할지 고려해보라.딕셔너리는 파이썬에서 매우 중요한 자료형으로, 키-값 쌍으로 데이터를 저장하고 관리하는 데 사용된다. 딕셔너리를 이해하고 활용하는 것은 파이썬 프로그래밍에서 필수적이다.
딕셔너리를 만들고, 값을 추가하고, 삭제하고, 조회하는 방법을 익히는 것은 파이썬 프로그래밍의 기초를 다지는 데 중요한 역할을 한다.
딕셔너리 뷰를 사용하여 키, 값, 키, 값
쌍에 접근하는 방법과 셋(set)과 비슷한 동작을 하는 것을 이해하는 것도 중요하다. 딕셔너리 컴프리헨션을 사용하여 딕셔너리를 간결하게 만드는 방법과, 딕셔너리 언팩킹을 통해 딕셔너리를 다른 딕셔너리에 추가하는 방법도 알아두면 유용하다.
딕셔너리에서 키가 없는 경우를 처리하는 방법으로는 in
연산자를 사용하거나, get()
메서드, setdefault()
메서드, defaultdict
를 사용하는 방법이 있다. 이러한 방법들을 적절히 활용하여 딕셔너리를 효과적으로 관리할 수 있다.