Recommanded Free YOUTUBE Lecture: <% selectedImage[1] %>

Contents

Dictionaries

딕셔너리는 리스트와 비슷하지만 더 범용적으로 사용 할 수 있다. 리스느는 인덱스로 정수만 사용 할 수 있지만 딕셔너리는 거의 모든 타입을 인덱스로 사용 할 수 있다.

딕셔너리는 값을 저장하는(혹은 가리키는) 키의 조합이라고 생각하면 된다. 키(key)와 값(value)로 이루어져있기 때문에 key-value pair라고 부른다. 혹은 아이텀(item)이라고 부르기도 한다. 예를 들어 한영사전을 딕셔너리로 만든다고 하면 키는 영어 문자열 값은 한글 문자열이 된다.

내장 함수인 dict를 이용해서 아이템을 가지지 않은 빈 딕셔너리를 만들 수 있다.
>>> eng2kor=dict()
>>> print (eng2kor)
{}
딕셔너리는 괄호"{}" 안에 아이템을 두는 방식으로 표시한다. 아이템의 추가는 대괄호를 이용하면 된다.
>>> eng2kor['apple']='사과'
우리는 키가 "apple"이고 값이 "사과"인 아이템을 딕셔너리에 추가했다. print로 출력해보면 추가된 아이템을 확인 할 수 있다.
>>> print (eng2kor)
{'apple': '사과'}
이제 3개의 아이템을 가진 딕셔너리를 만들어보자.
>>> eng2kor={'apple':'사과', 'house':'집', 'dog':'개'}
>>> print (eng2kor)
{'apple': '사과', 'house': '집', 'dog': '개'}
딕셔너리에 있는 아이템들은 정렬(sort)되지 않는다. 출력 결과의 순서는 컴퓨터마다 다를 수 있으며, 예측 할 수 없다. 하지만 리스트 처럼 정수를 인덱스로 사용하는게 아니기 때문에 정렬여부는 크게 중요하지는 않다. 인덱스 값대신 키를 사용해서 값을 찾을 수 있다.
>>> print (eng2kor['house'])
집
딕셔너리에 존재하지 않는 키로 찾으려고 하면 예러를 출력한다.
>>> print (eng2kor['sky'])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'sky'
len 함수를 이용해서 딕셔너리가 가지고 있는 아이템의 갯수를 알 수 있다.
>>> len(eng2kor)
3
딕셔너리에서 제공하는 in 연산자를 이용해서 딕셔너리에 키가 있는지를 검사 할 수 있다.
>>> 'sky' in eng2kor
False
>>> 'house' in eng2kor
True
리스트에도 in 연산자가 있는데, 딕셔너리와는 완전히 다른 알고리즘을 사용한다. 리스트의 경우 목록이 길어지면 검색 시간이 정비례해서 길어진다. 하지만 사전의 경우 해시 알고리즘을 사용하므로 사전에 아이템이 얼마가 있더라도 거의 동일한 시간에 원하는 아이템을 찾을 수 있다.

Dictionary as a set of counters

문자열에서 각 문자가 몇 번 나타났는지를 계산하는 프로그램을 만들려고 한다. 몇 가지 방법이 있다.
  1. 알파벳의 각 문자에 하나씩 26개의 변수를 만든다. 그런 다음 문자열을 탐색하면서 각 문자에 연결된 카운터를 증가 한다.
  2. 26개의 요소를 저장할 수 있는 리스트를 만든다. 그리고 ord 함수를 이용해서 각 문자를 숫자로 변환한 다음, 리스트의 인덱스의 카운터를 증가한다.
  3. 알파벳 문자를 키로 하고 카운터를 값으로 하는 딕셔너리를 만든다. 처음 등장한 문자는 값을 0으로 해서 딕셔너리에 추가하고, 딕셔너리에 있는 문자라면 값+1 을 한다.
아래 딕셔너리를 이용한 구현이 있다.
def histogram(s):
    d = dict()
    for c in s:
        if c not in d:
            d[c]=1
        else:
            d[c] += 1
    return d

print histogram("Hello World")
		
함수의 이름은 histogram으로 문자열 s에 각 문자가 몇 번 출현했는지를 계산한다. l은 3번 o는 두번 등장했다는 것을 알 수 있다.

딕셔너리와 루프

for문을 이용해서 딕셔너리의 아이템을 순환 할 수 있다. 아래 print_hist 함수는 딕셔너리를 순환하면서 키와 값을 출력한다.
def histogram(s):
    d = dict()
    for c in s:
        if c not in d:
            d[c] = 1
        else:
            d[c] += 1
    return d

def print_hist(h):
    for c in h:
        print c, "->", h[c]

h = histogram('Helloworld')
print_hist(h)
	
		

Reverse 루프

딕셔너리는 를 이용해서 쉽게 값을 찾을 수 있다. 이 연산을 lookup이라고 부른다.

반대로 을 이용해서 를 찾으려면 어떻게 해야 할까. 여기에는 두가지 문제가 있다. 첫째 같은 값을 가지는 키가 두개 이상 있을 수 있다. 응용 프로그램에 따라서 처음 발견한 하나만 돌려주거나 키의 목록을 만들어야 할 수도 있다. 둘째, 값으로 조회하는 효과적인 알고리즘이 없다. 딕셔너리에 있는 모든 아이템을 전부 조회해야 한다.

아래 함수는 값을 가지고 있는 첫번째 키만 돌려준다.
def reverse_lookup(d, v):
    for k in d:
        if d[k] == v:
            return k
    raise ValueError
딕셔너리에서 값을 하나도 찾을 수 없다면 ValueError예외를 발생한다.

아래는 값을 찾는데 성공한 예제다.
>>> h = histogram('parrot')
>>> k = reverse_lookup(h, 2)
>>> print k
r
값을 찾지 못했다면 아래와 같이 예외를 발생한다.
>>> k = reverse_lookup(h, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "<stdin>", line 5, in reverse_lookup
ValueError
에러 원인을 좀더 자세히 기술 할 수도 있다.
>>> raise ValueError('value does not appear in the dictionary')
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
ValueError: value does not appear in the dictionary

딕셔너리와 리스트

딕셔너리의 값으로 리스트를 저장 할 수도 있다. 앞선 reverse_lookup의 키로 주어지는 발생빈도를 가지는 하나의 문자만을 반환하는데, 같은 발생빈도를 가지는 모든 문자들을 리스트로 반환하는 코드를 만들 수 있다. 아래 예제를 보자.
def histogram(s):
    d = dict()
    for c in s:
        if c not in d:
            d[c] = 1
        else:
            d[c] += 1
    return d
    
def invert_dict(d):
    inverse = dict()
    for key in d:
        val = d[key]
        if val not in inverse:
            inverse[val] = [key]
        else:
            inverse[val].append(key)
    return inverse
    
hist = histogram('paroot')
print hist

inverse = invert_dict(hist)
print inverse
	
		
루프를 통과 할 때마다 histogram 딕셔너리에서 키와 값을 가져온다. 이렇게 가져온 값을 키로 하는 inverse 딕셔너리를 만들고 여기에 키를 리스트로 append 한다. 이 과정을 상태 다이어그램(state digram)으로 묘사했다.

 State digram

리스트는 딕셔너리의 값이 될 수는 있지만 키가 될 수는 없다. 아래 코드를 실행해보자.
>>> t = [1, 2, 3]
>>> d = dict()
>>> d[t] = 'oops'
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: list objects are unhashable
딕셔너리는 키의 해시테이블을 유지한다고 했다. 따라서 키로 사용하려면 해시 테이블로 만들 수 있어야 한다. 키를 목록으로 사용한다고 가정해보자. 키가 불면이라면 올바르게 작동할 것이다. 그러나 목록의 경우 키를 변경 할 수 있는데, 그때 나쁜 일이 발생한다. 파이선은 키-값 쌍을 만들 때 해시를 하는데, 키가 수정되면 다른 해시 값을 가질 수 있다. 이 경우 동일한 키에 대해서 두개의 항목이 있거나 키를 찾지 못할 수도 있다.

리스트와 딕셔너리는 변경 할 수 있으므로 키로 사용 할 수 없다. 다만 값으로만 사용 할 수 있다.

Memos

전역 변수

Long integers

fibonacci(50의 계산 결과를 보자.
>>> fibonacci(50)
12586269025L
정수의 마지막에 붙어 있는 L은 데이터 타입이 long interger 임을 나타낸다. 파이선 3에서는 long 이 사라졌다. 대신 int 타입이 long까지를 모두 포함한다.