딕셔너리(dictionary)는 사전이라는 의미입니다. 사전에는 단어와 단어의 설명이 저장되어 있습니다.
파이썬의 딕셔너리는 키(key)와 값(value)의 쌍을 저장할 수 있는 객체입니다. 우리는 키를 이용하여 값을 검색할 수 있습니다.
| 키(key) | 값(value) |
| "Kim" | "01012345678" |
| "Park" | "01012345679" |
| "Lee" | "01012345680" |
딕셔너리에서 키는 해시 가능한 객체여야 하고 중복되는 값은 허용되지 않습니다.
딕셔너리는 중괄호 안에 항목을 쉼표로 분리시켜 나열하면 됩니다. 항목은 키(key)와 값(value)으로 구성됩니다.
딕셔너리 = { 키1:값1, 키2:값2, ... }
값은 어떤 객체이든지 가능하지만 키는 변경 불가능한 객쳐여야 합니다. 즉 문자열이나 숫자여야 합니다.
만약 딕셔너리에서 키가 변경 가능하다면 많은 문제가 발생되기 때문입니다.
d = {1:'apple', 2:'banana'}
위의 문장은 2개의 항목을 가진 딕셔너리를 생성합니다. 각 항목은 "키:값"의 형식으로 구성됩니다.
첫 번째 항목의 키는 1이고 값은 'apple'입니다. 두 번째 항목의 키는 2이고 값은 'banana'입니다. 키의 자료형은 혼합되어도 됩니다.
예를 들어 사람들의 이름과 전화번호를 딕셔너리로 저장해보면 다음과 같이 될 것입니다.
contacts = {'Kim':'01012345678', 'Park':'01012345679', 'Lee':'01012345680'}
print(contacts)
<실행 결과>
{'Kim': '01012345678', 'Park': '01012345679', 'Lee': '01012345680'}
공백 딕셔너리는 {}로 생성합니다.
d = {}
그런데 {}은 수학에서 집합을 나타내는 기호이죠?
파이썬에서는 집합보다는 딕셔너리가 많이 사용되기 때문에 {}은 공백 딕셔너리를 나타내고 공백 집합은 set()을 사용해서 생성합니다.
딕셔너리에서 항목을 꺼낼 때는 항목의 키를 사용하면 됩니다. 키를 [] 안에 지정하든지 아니면 get() 메소드를 사용해도 됩니다.
예를 들어 연락처에 이름이 "Kim"인 사람의 전화번호를 꺼내려면 다음과 같은 문장을 사용합니다.
contacts = {'Kim':'01012345678', 'Park':'01012345679', 'Lee':'01012345680'}
print(contacts['Kim'])
print(contacts.get('Park'))
<실행 결과>
01012345678
01012345679
만약 키가 딕셔너리에 없으면 KeyError가 발생합니다. get()을 사용했을 때, 키가 없으면 None이 반환됩니다.
키가 없을 때 디폴트 값을 사용하려면 get()의 두 번째 인수로 디폴트 값을 전달합니다.
contacts = {'Kim':'01012345678', 'Park':'01012345679', 'Lee':'01012345680'}
print(contacts.get('Choi', '010114'))
<실행 결과>
010114
키가 딕셔너리에 있는지를 알려면 in 연산자를 사용합니다.
contacts = {'Kim':'01012345678', 'Park':'01012345679', 'Lee':'01012345680'}
if "Kim" in contacts:
print("키가 딕셔너리에 있습니다")
<실행 결과>
키가 딕셔너리에 있습니다
딕셔너리도 변경 가능한 컨테이너입니다. 따라서 우리는 딕셔너리에 항목을 추가하거나 삭제할 수 있습니다.
예를 들어서 연락처 딕셔너리에 새로운 연락처를 등록하려면 다음과 같이 합니다.
contacts = {'Kim':'01012345678', 'Park':'01012345679', 'Lee':'01012345680'}
contacts['Choi'] = '01056781234'
print(contacts)
<실행 결과>
{'Kim': '01012345678', 'Park': '01012345679', 'Lee': '01012345680', 'Choi': '01056781234'}
딕셔너리에서 특정한 항목은 pop()을 호출하여 삭제할 수 있습니다. pop() 메소드는 주어진 키에 해당되는 항목을 삭제하고 항목의 값을 반환합니다.
contacts = {'Kim':'01012345678', 'Park':'01012345679', 'Lee':'01012345680'}
contacts.pop("Kim")
print(contacts)
<실행 결과>
{'Park': '01012345679', 'Lee': '01012345680'}
아니면 del 키워드를 이용하여도 됩니다.
contacts = {'Kim':'01012345678', 'Park':'01012345679', 'Lee':'01012345680'}
del contacts["Kim"]
print(contacts)
<실행 결과>
{'Park': '01012345679', 'Lee': '01012345680'}
딕셔너리의 모든 항목을 삭제하려면 clear()를 사용합니다.
딕셔너리에 저장된 항목을 순차적으로 순회하려면 for 루프를 사용하면 됩니다.
scores = {'Korean':80, 'Math':90, 'English':80}
for item in scores.items():
print(item)
<실행 결과>
('Korean', 80)
('Math', 90)
('English', 80)
위의 코드에서 for item in scores을 사용하면 키만 출력되므로 items() 메소드를 사용하여 키와 값을 함께 출력하도록 변경하였습니다.
어떤 키가 딕셔너리에 있는지를 알려면 in 연산자를 사용합니다.
squares = {1:1, 3:9, 5:25, 7:49, 9:81}
print(1 in squares)
print(2 not in squares)
<실행 결과>
True
True
딕셔너리 함축(Dictionary Comprehension)은 새로운 딕셔너리를 생성하는 우아하고 간결한 방법입니다. 6장의 리스트 함축과 매우 유사합니다.
딕셔너리 함축은 {} 안의 (key: value) 와 for 문장으로 이루어집니다. 아래는 0에서 5까지의 정수로부터 3 제곱값을 생성하는 코드입니다.
triples = { x: x*x*x for x in range(6) }
print(triples)
<실행 결과>
{0: 0, 1: 1, 2: 8, 3: 27, 4: 64, 5: 125}
딕셔너리의 키를 정렬하려면 가장 간단한 방법은 파이썬의 내장 함수인 sorted() 함수를 사용하는 것입니다.
sorted() 함수는 딕셔너리 객체를 받아서 정렬된 키들의 리스트를 반환합니다.
dic = { "cups" : 2, "books" : 5, "pens" : 3, "bags" : 1, "bottles" : 4, "coins" : 7 }
print(sorted(dic))
<실행 결과>
['bags', 'books', 'bottles', 'coins', 'cups', 'pens']
만약 딕셔너리의 값을 정렬하고 싶으면 values() 메소드를 사용하여 아래와 같이 합니다.
dic = { "cups" : 2, "books" : 5, "pens" : 3, "bags" : 1, "bottles" : 4, "coins" : 7 }
print(sorted(dic.values()))
<실행 결과>
[1, 2, 3, 4, 5, 7]
만약 딕셔너리의 값에 따라서 키들을 정렬하고 싶으면 sorted() 함수에 요소들을 비교할 때 사용하는 키를 지정하여야 합니다.
dic = { "cups" : 2, "books" : 5, "pens" : 3, "bags" : 1, "bottles" : 4, "coins" : 7 }
# [1, 2, 3, 4, 5, 7] 순서로 정렬됩니다.
print(sorted(dic, key = dic.__getitem__))
<실행 결과>
['bags', 'cups', 'pens', 'bottles', 'books', 'coins']
1. 딕셔너리의 첫 번째 용도는 말 그대로 사전을 만드는 것입니다. 우리는 영한 사전을 구현하여 봅시다.
공백 딕셔너리를 생성하고 여기에 영어 단어를 키로 하고 설명을 값으로 하여 저장하면 될 것입니다.
| 키 (Key) | 값 (Value) |
| redvelvet | 레드벨벳 |
| irene | 아이린 |
| seulgi | 슬기 |
| wendy | 웬디 |
| joy | 조이 |
| yeri | 예리 |
<실행 결과>
단어를 입력하세요 : redvelvet
레드벨벳
단어를 입력하세요 : twice
없음
2. 사용자가 지정하는 파일을 읽어서 파일에 저장된 각각의 단어가 몇 번이나 나오는지를 계산하는 프로그램을 작성해봅시다.
아래의 실행 결과는 이전에 작성했던 CountingStars.txt 를 이용하였습니다.
<실행 결과>
파일 이름 : CountingStars.txt
{'Lately': 7, 'I': 39, 'been,': 14, 'been': 14, 'losing': 7, 'sleep': 7, 'Dreaming': 7, 'about': 2, 'the': 22, 'things': 7, 'that': 19, 'we': 7, 'could': 13, 'be': 15, 'But': 5, 'baby': 3, "prayin'": 7, 'hard': 7, 'Said': 7, 'no': 7, 'more': 7, 'counting': 15, 'dollars': 7, "We'll": 7, 'stars': 8, 'Yeah,': 1, "we'll": 4, 'see': 1, 'this': 2, 'life': 1, 'Like': 1, 'a': 2, 'swinging': 1, 'vine': 1, 'Swing': 1, 'my': 2, 'heart': 1, 'across': 1, 'line': 1, 'In': 1, 'faces': 1, 'flashing': 1, 'signs': 1, 'Seek': 1, 'it': 7, 'out': 1, 'and': 5, 'ye': 1, 'shall': 1, 'find': 1, 'The': 1, 'old,': 1, 'but': 4, "I'm": 6, 'not': 4, 'old': 2, 'Young,': 2, 'bold': 2, 'And': 3, "don't": 2, 'think': 2, 'world': 2, 'is': 3, 'sold': 2, 'just': 2, 'doing': 4, 'what': 2, "we're": 2, 'told': 2, 'I,': 3, 'feel': 7, 'something': 3, 'so': 3, 'right': 3, 'Doing': 1, 'wrong': 3, 'thing': 3, 'lie,': 4, 'lie': 2, 'Everything': 3, 'kills': 2, 'me': 6, 'makes': 3, 'alive': 2, "'bout": 5, 'Baby': 4, 'be,': 3, 'love': 1, 'burn': 6, 'Down': 1, 'river': 5, 'every': 1, 'turn': 1, 'Hope': 1, 'four': 1, 'letter': 1, 'word': 1, 'Make': 1, 'money': 5, 'Watch': 1, 'Old,': 1, 'drowns': 1, 'wanna': 1, 'fly': 1, 'Take': 4, 'watch': 4, 'Sink': 4, 'in': 4, 'lessons': 4, 'learned': 4}
3. 봉민이는 평소 일상 생활에서 이상한 축약어를 많이 사용합니다.
예를 들어 "갑붕싸(갑자기 붕어 싸만코)" "점술회(점심에 술마시는 모임(會))" "봉커톤(봉민이 과제 도와주는 노예짓)" "똘머(똘똘이 스머프)" 와 같은 축약어들이 있습니다.
이런 이상한 말을 쓰고 다니는 봉민이와 원활한 대화를 하기 위해 축약어를 풀어서 일반적인 문장으로 변환하는 프로그램을 작성하여 봅시다.
<실행 결과>
번역할 문장을 입력하세요 : 점술회 회원 모집 (3/100)
점심에 술마시는 모임(會) 회원 모집 (3/100)
번역할 문장을 입력하세요 : 형 오늘 봉커톤 뛰실?
형 오늘 봉민이 과제 도와주는 노예짓 뛰실?
4.문자열 안에서 각 글자들이 나타나는 빈도수를 계산하여 봅시다. 글자들의 빈도수를 알면 문자열을 압축할 때, 유용하게 사용될 수 있습니다.
예를 들어 허프만 코딩은 글자의 빈도수에 따라서 글자를 나타내는 코드의 길이를 다르게 합니다. 즉 많이 나타나는 글자에는 짧은 코드를 할당하고 자주 나오지 않는 글자에는 긴 코드를 할당하는 것입니다.

딕셔너리는 이러한 빈도 테이블을 구현하는데 아주 이상적입니다. 문자열을 입력받아서 각 문자들의 빈도를 빈도수가 많은 순서대로 출력하는 프로그램을 작성해봅시다.
<실행 결과>
문자열을 입력하세요 : Red velvet is one of the most famous idol girlgroup in the universe
[(' ', 12), ('e', 8), ('o', 6), ('i', 5), ('t', 4), ('s', 4), ('v', 3), ('l', 3), ('n', 3), ('u', 3), ('r', 3), ('d', 2), ('f', 2), ('h', 2), ('m', 2), ('g', 2), ('R', 1), ('a', 1), ('p', 1)]
구성 요소가 튜플로 이뤄진 리스트 letter_items를 내림차순으로 정렬하는 코드는 아래와 같습니다.
letter_items.sort(key=lambda tup: tup[1], reverse=True)
5. 순환을 사용하게 되면 단순하게 작성이 가능하며 가독성이 높아집니다. 그러나 똑같은 계산을 반복하면 단순할지라도 계산시간이 엄청나게 길어질 수 있습니다.
이러한 예로 순환 호출을 이용하여 피보나치 수열을 계산해 봅시다. 피보나치 수열이란 다음과 같이 정의되는 수열입니다.
즉 일반적인 경우, 앞의 두 개의 숫자를 더해서 뒤의 숫자를 만들면 됩니다. 정의에 따라 수열을 만들어 보면 다음과 같습니다.
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...
피보나치 수열은 정의 자체가 순환적으로 되어 있습니다. 따라서 구현시에 순환 호출을 사용하는 것이 자연스러운 방법입니다.
피보나치 수열을 파이썬을 이용하여 프로그램해보면 다음과 같습니다.
def fib(n):
if n == 1 or n == 2:
return 1
return fib(n-1) + fib(n-2)
print(fib(20))
<실행 결과>
6765
위의 함수는 매우 단순하고 이해하기 쉽지만 매우 비효율적입니다. 왜 그럴까요? 예를 들어 fib(6)으로 호출하였을 경우 fib(4)가 두 번이나 계산되기 때문입니다.
fib(3)은 3번 계산되고 이런 현상은 순환호출이 깊어질수록 점점 심해집니다. 따라서 상당히 비효율적임을 알 수 있습니다.
여기서 fib(6)을 구하기 위하여 fib() 함수가 25번이나 호출되는 것에 유의하여야 합니다.
근본적인 이유는 중간에 계산되었던 값을 기억하지 않고 다시 계산을 하기 때문입니다. 예를 들어 fib(6)을 구하기 위한 과정에서 fib(3)은 3번이나 별도로 계산됩니다.
따라서 n이 커지면 순환호출을 사용하여 피보나치 수열을 계산하는 것은 거의 불가능합니다. 그렇다면 피보나치 수열을 계산하는데 다른 방법이 있을까요?
이 경우에는 순환을 사용하지 않고 반복구조를 이용하여 프로그램하면 제일 좋은 결과를 얻을 수 있습니다. 하지만 딕셔너리를 사용할 수도 있습니다.
딕셔너리를 사용하여 피보나치 수열 프로그램을 작성해봅시다.
table이라는 이름의 딕셔너리에 우리가 이미 계산한 피보나치 값들을 저장합니다. fib() 함수가 호출되면 딕셔너리에 값들이 있는지를 검사합니다.
딕셔너리에 값이 있으면 우리가 이전에 계산한 것이므로 딕셔너리에서 값을 찾아서 반환하면 됩니다.
만약 딕셔너리에 없으면 새롭게 피보나치 수열을 계산하여서 딕셔너리에 저장한 후에 값을 반환합니다. 이런 식으로 하면 fib(100)도 순식간에 계산할 수 있습니다.
<실행 결과>
354224848179261915075
6. 행렬(matrix)은 자연과학에서 많은 문제를 해결하는데 사용됩니다. 따라서 행렬을 프로그램에서 표현하는 것은 중요한 문제입니다.
행렬을 어떻게 표현할 것인지를 생각해봅시다. 일반적으로 행렬을 표현하는 자연스러운 방법은 다음과 같은 2차원 리스트를 사용하는 것입니다.
matrix = [ [0, 0, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 2, 0, 0],
[0, 0, 3, 0, 0],
[0, 0, 0, 0, 0] ]
이 방법을 방법 1이라고 합시다. 많은 항들이 0으로 되어 있는 희소행렬인 경우에는 메모리의 낭비가 심하게 됩니다.
더구나 엄청난 크기의 희소행렬인 경우에는 컴파일러에 따라 사용하지 못하는 경우도 있습니다.
따라서 희소 행렬을 표현하는 다른 방법을 생각해봅시다. 한 가지의 방법은 0이 아닌 요소들만을 나타내는 방법입니다.
이 방법을 방법 2라고 합시다. 즉 0이 아닌 노드만을 "(행, 열) : 값"으로 표시하는 것입니다.
딕셔너리를 이용하여 앞의 희소 행렬을 두 번째 방법처럼 저장하고 추출하는 프로그램을 작성해봅시다.
<출력 결과>
0 0 0 0 0
0 0 1 0 0
0 0 2 0 0
0 0 3 0 0
0 0 0 0 0
희소 행렬에서 요소를 추출하려면 [ ] 연산자를 사용하면 되지만 문제가 있습니다. 우리가 키를 (3, 3) 으로 주고 요소를 [ ] 연산자로 추출하면 오류가 발생합니다.
왜냐하면 딕셔너리에 (3, 3)이란 키가 없기 때문입니다. 이 문제를 해결하기 위해서는 get() 메소드를 사용하여야 합니다.
get() 메소드의 첫 번째 인수는 키이고 두 번째 인수는 키가 딕셔너리에 없는 경우에 반환되는 값입니다.