2014 PyCon KR: 위대한 dict 이해하고 사용하기

Aa077de7164201e19f9ebc15713797b8?s=47 jongman
August 30, 2014

2014 PyCon KR: 위대한 dict 이해하고 사용하기

프리젠테이션의 링크는 pdf 버전을 다운받으시면 보실 수 있습니다.

Aa077de7164201e19f9ebc15713797b8?s=128

jongman

August 30, 2014
Tweet

Transcript

  1. 2.

    자기소개 10 년차 개발자 ( 파이썬은 대략 6 년 )

    퀀트 개발자 ( ≈ 풀타임 파이썬 ) 알고리즘 문제 해결 전략 ('11) algospot.com 운영진 ('07~) 2 / 73
  2. 3.

    오늘의 주제 1. dict 기초 사용 2. dict 동작 원리

    3. 이디엄들과 응용 클래스들 3 / 73
  3. 4.

    이 톡 왜 하나요 ? 이론적인 흥미 이런 유용한 자료

    구조를 어떻게 만들었을까 ? d i c t 를 사용하며 발생하는 문제들은 왜 생길까 ? 실용적인 흥미 코드를 쉽게 만들어 줄 수 있는 이디엄과 응용 클래 스들 4 / 73
  4. 6.

    dict = 연관 매핑 > > > d = {

    ' j a n ' : 1 , ' f e b ' : 2 , ' m a r ' : 3 } > > > p r i n t d [ ' j a n ' ] , d [ ' f e b ' ] , d [ ' m a r ' ] 1 2 3 " 사전 " 자료 구조 키 (key) 와 값 (value) 사이의 관계를 저장한다 6 / 73
  5. 7.

    만들기 # d i c t 리터럴 > > >

    { ' j a n ' : 1 , ' f e b ' : 2 , ' m a r ' : 3 , . . . } { ' a p r ' : 4 , ' a u g ' : 8 , ' d e c ' : 1 2 , ' f e b ' : 2 , . . } # d i c t ( ) 의 키워드 인자 > > > d i c t ( j a n = 1 , f e b = 2 , m a r = 3 , a p r = 4 , . . ) { ' a p r ' : 4 , ' a u g ' : 8 , ' d e c ' : 1 2 , ' f e b ' : 2 , . . } # ( k e y , v a l u e ) 쌍의 목록으로 만들기 > > > m o n t h _ n a m e s = [ ( ' j a n ' , 1 ) , ( ' f e b ' , 2 ) , ( ' m a r ' , 3 ) , ( ' a p r ' , 4 ) , ( ' m a y ' , 5 ) , ( ' j u n ' , 6 ) , . . ] > > > d i c t ( m o n t h _ n a m e s ) { ' a p r ' : 4 , ' a u g ' : 8 , ' d e c ' : 1 2 , ' f e b ' : 2 , . . } 7 / 73
  6. 8.

    Dictionary Comprehension # 키나 값 변경하기 > > > {

    n a m e . t i t l e ( ) : n u m f o r n a m e , n u m i n m o n t h _ n a m e s } { ' M a r ' : 3 , ' F e b ' : 2 , ' A u g ' : 8 , ' S e p ' : 9 , . . } # 짝수 달만 고르기 > > > { n a m e : n u m f o r n a m e , n u m i n m o n t h _ n a m e s i f n u m % 2 = = 0 } { ' f e b ' : 2 , ' a u g ' : 8 , ' a p r ' : 4 , ' j u n ' : 6 , . . } # d i c t 뒤집기 > > > m o n t h s = d i c t ( m o n t h _ n a m e s ) > > > { v : k f o r k , v i n m o n t h s . i t e m s ( ) } { 1 : ' j a n ' , 2 : ' f e b ' , 3 : ' m a r ' , 4 : ' a p r ' , . . } 8 / 73
  7. 9.

    탐색 > > > m o n t h s

    = { ' j a n ' : 1 , ' f e b ' : 2 , ' m a r ' : 3 , ' a p r ' : 4 , . . } > > > m o n t h s [ ' j a n ' ] 1 > > > m o n t h s [ ' J a n ' ] K e y E r r o r : J a n > > > ' J a n ' i n m o n t h s F a l s e 9 / 73
  8. 10.

    get() / setdefault() > > > m o n t

    h s . g e t ( ' J a n ' ) N o n e > > > m o n t h s . g e t ( ' J a n ' , 1 ) 1 > > > m o n t h s [ ' J a n ' ] K e y E r r o r : J a n > > > m o n t h s . s e t d e f a u l t ( ' J a n ' , 1 ) 1 > > > m o n t h s [ ' J a n ' ] 1 10 / 73
  9. 11.

    예제 : 길이 분포 구하기 > > > w o

    r d s = o p e n ( ' / u s r / s h a r e / d i c t / w o r d s ' ) . r e a d ( ) . s p l i t l i n e s ( ) > > > w o r d s [ ' A ' , ' a ' , ' a a ' , ' a a l ' , ' a a l i i ' , ' a a m ' , ' A a n i ' , ' a a r d v a r k ' , . . ] 목표 : 단어 길이의 분포를 구해 보자 길이 1 인 단어 : 52 개 길이 2 인 단어 : 160 개 길이 3 인 단어 : 1420 개 .. 11 / 73
  10. 12.

    예제 : 길이 분포 구하기 > > > l e

    n g t h _ c o u n t = { } > > > f o r l i n m a p ( l e n , w o r d s ) : i f l i n l e n g t h _ c o u n t : l e n g t h _ c o u n t [ l ] + = 1 e l s e : l e n g t h _ c o u n t [ l ] = 1 12 / 73
  11. 13.

    예제 : 길이 분포 구하기 > > > l e

    n g t h _ c o u n t = { } > > > f o r l i n m a p ( l e n , w o r d s ) : l e n g t h _ c o u n t [ l ] = l e n g t h _ c o u n t . g e t ( l , 0 ) + 1 13 / 73
  12. 14.

    예제 : 길이별 목록 구하기 아예 목록을 구해 보자 !

    > > > b y _ l e n = { } > > > f o r w i n w o r d s : b y _ l e n . g e t ( l e n ( w ) , [ ] ) . a p p e n d ( w ) > > > b y _ l e n { } # . . . 어? 14 / 73
  13. 15.

    예제 : 길이별 목록 구하기 setdefault() 로 해결 ! >

    > > b y _ l e n = { } > > > f o r w i n w o r d s : b y _ l e n . s e t d e f a u l t ( l e n ( w ) , [ ] ) . a p p e n d ( w ) > > > b y _ l e n { 1 : [ ' A ' , ' a ' , ' B ' , ' b ' , ' C ' , . . . ] , 2 : [ ' a a ' , ' A b ' , ' a d ' , ' a e ' , ' A h ' , . . . ] , 3 : [ ' a a l ' , ' a a m ' , ' a b a ' , ' a b b ' , ' A b e ' , . . ] , . . } 15 / 73
  14. 16.

    삭제 > > > d e l m o n

    t h s [ ' m a r ' ] > > > ' m a r ' i n m o n t h s F a l s e 16 / 73
  15. 17.

    순회 > > > m o n t h s

    . k e y s ( ) [ ' m a r ' , ' f e b ' , ' a u g ' , ' s e p ' , . . ] > > > [ m o n t h f o r m o n t h i n m o n t h s ] [ ' m a r ' , ' f e b ' , ' a u g ' , ' s e p ' , . . ] > > > m o n t h s . v a l u e s ( ) [ 3 , 2 , 8 , 9 , . . ] > > > m o n t h s . i t e m s ( ) [ ( ' m a r ' , 3 ) , ( ' f e b ' , 2 ) , ( ' a u g ' , 8 ) , ( ' s e p ' , 9 ) , . . ] 순서는 엉망진창 ! i t e r k e y s ( ) , i t e r v a l u e s ( ) , i t e r i t e m s ( ) 17 / 73
  16. 19.

    요구 조건 추가 / 검색 / 삭제 / 순회 연산을

    지원할 것 가능한한 : 빠른 속도 적은 메모리 사용량 19 / 73
  17. 20.

    속도란 무엇일까 ? dict 를 구현한 4 개의 서로 다른

    클래스가 있다고 하자 각 클래스 인스턴스를 생성한 뒤 0~19 까지의 키를 넣고 이 중 임의의 한 키를 찾는 연산을 10 만번 반복 각 클래스마다 평균 소요 시간을 반환한다 . 20 / 73
  18. 21.

    결과 dict 구현 평균시간 A 2ns B 5ns C 3ns

    D 0.5ns D 가 가장 좋은 구현처럼 보인다 ! 그러나 진실은 저 멀리에 .. 21 / 73
  19. 22.

    방법론의 문제 ( 엄청나게 여러 가지 문제가 있지만 ) 가장

    큰 문제 : dict 의 크기에 따라 속도는 변화한다 ! 22 / 73
  20. 23.

    속도의 2 가지 그림자 d i c t 의 크기가

    작을 때 얼마나 빠르게 동작하는가 ? → 프로그램 최적화 중복 연산 제거 , 캐시 친화적 자료 구조 , .. d i c t 의 크기가 커질 때 속도가 어떻게 변화하는가 ? → 자료를 어떠한 형태로 저장하는가 ? 오늘의 주제 23 / 73
  21. 24.

    어떻게 자료를 저장하나 ? 인덱스 키 값 0 'jan' 1

    1 'feb' 2 2 'mar' 3 3 'apr' 4 4 'may' 5 5 'jul' 6 6 'jun' 7 7 'aug' 8 .. .. .. 24 / 73
  22. 25.

    어떻게 자료를 저장하나 ? 인덱스 키 값 0 'apr' 4

    1 'aug' 8 2 'dec' 12 3 'feb' 2 4 'jan' 1 5 'jul' 6 6 'jun' 7 7 'mar' 3 .. .. .. 25 / 73
  23. 28.

    파이썬의 선택 해시테이블 (hash table) 을 사용 dict 의 크기와

    상관없이 일정한 속도를 유지 ! 흔한 사용처에 빠르게 동작하도록 수많은 최적화 (CPython 소스코드 참조 ) 28 / 73
  24. 31.

    해시 테이블 일부는 키가 들어 있고 , 일부는 비어 있는

    배열 인덱스 키 값 0 'jan' 1 1 (EMPTY) (EMPTY) 2 (EMPTY) (EMPTY) 3 'mar' 3 4 'feb' 2 5 (EMPTY) (EMPTY) 6 (EMPTY) (EMPTY) 7 (EMPTY) (EMPTY) 31 / 73
  25. 32.

    해시 함수 키를 갈아 넣으면 숫자가 나와요 > > >

    h a s h ( ' j a n ' ) - 8 6 8 1 4 1 9 8 8 3 2 2 4 6 2 2 0 3 2 > > > h a s h ( ' f e b ' ) - 4 1 7 7 1 9 7 8 3 3 2 0 1 1 9 0 6 2 0 > > > h a s h ( f l o a t ) - 9 2 2 3 3 7 2 0 3 6 5 7 4 9 6 1 4 2 0 > > > h a s h ( _ _ b u i l t i n s _ _ ) - 9 2 2 3 3 7 2 0 3 6 5 7 4 9 2 6 4 1 6 32 / 73
  26. 33.

    해시값 → 테이블 내의 위치 > > > h a

    s h ( ' j a n ' ) % 8 0 > > > h a s h ( ' f e b ' ) % 8 4 > > > h a s h ( ' m a r ' ) % 8 3 33 / 73
  27. 34.

    탐색이 간단하다 해시값 계산 후 바로 찾음 해당 자리가 비어

    있으면 해당 키가 없다 키 개수와 상관 없이 일정한 속도 ! 34 / 73
  28. 35.

    영향 : 순서가 엉망진창 순회는 키와 상관없이 배열의 0 부터

    n‑1 번까지 이 중 비어 있지 않은 곳을 반환 ! 35 / 73
  29. 38.

    충돌 : 문제 > > > h a s h

    ( ' j a n ' ) % 8 0 > > > h a s h ( ' a p r ' ) % 8 0 'apr' 은 어디에 넣지 ? 38 / 73
  30. 39.

    충돌 해결 : 체이닝 테이블의 각 자리에 연결 리스트를 넣어둔다

    인덱스 ( 키 , 값 ) 0 [('jan', 1), ('apr', 4)] 1 [] 2 [] 3 [('mar', 3)] 4 [('feb', 2)] 5 [] 6 [] 7 [] 39 / 73
  31. 40.

    충돌 해결 : 체이닝 테이블의 각 자리에 연결 리스트를 넣어둔다

    인덱스 ( 키 , 값 ) 0 [('jan', 1), ('apr', 4), ('may', 5)] 1 [] 2 [('nov', 11)] 3 [('mar', 3), ('dec', 12)] 4 [('feb', 2), ('jun', 6)] 5 [('oct', 10)] 6 [('jul', 7), ('aug', 8)] 7 [('sep', 9)] 40 / 73
  32. 41.

    체이닝 : 장단점 ( 장점 ) 간단하다 ( 단점 )

    한 리스트가 커지면 느려진다 대부분 자리에 한두 개의 키만 포함되도록 조절 ( 단점 ) 느리다 : 연결 리스트를 유지해야 함 메모리 할당 비용 캐시 로컬리티 41 / 73
  33. 42.

    충돌 해결 : 개방 주소 (CPython) 그 다음(*) 자리에 넣는다

    ! 인덱스 키 값 0 'jan' 1 1 'apr' 4 2 (EMPTY) (EMPTY) 3 'mar' 3 4 'feb' 2 5 (EMPTY) (EMPTY) 6 (EMPTY) (EMPTY) 7 (EMPTY) (EMPTY) 42 / 73
  34. 43.

    영향 : 탐색도 까다로워요 다른 키가 들어 있으면 그 다음

    자리도 봐야 함 원하는 키를 찾거나 , 빈 자리가 있어야 결론 빈 자리가 적으면 한바퀴를 다 돌아서야 확인 가능 빈 자리의 비율 (load factor) 이 중요해진다 43 / 73
  35. 44.

    영향 : 삭제가 까다로워요 d e l m o n

    t h s [ ' j a n ' ] 인덱스 키 값 0 (EMPTY) (EMPTY) 1 'apr' 4 2 (EMPTY) (EMPTY) 3 'mar' 3 4 'feb' 2 5 (EMPTY) (EMPTY) 6 (EMPTY) (EMPTY) 7 (EMPTY) (EMPTY) 44 / 73
  36. 45.

    영향 : 삭제가 까다로워요 " 변수가 여기 있었다 " 인덱스

    키 값 0 *dummy* *dummy* 1 'apr' 4 2 (EMPTY) (EMPTY) 3 'mar' 3 4 'feb' 2 5 (EMPTY) (EMPTY) 6 (EMPTY) (EMPTY) 7 (EMPTY) (EMPTY) 45 / 73
  37. 46.

    영향 : 순서가 달라요 같은 사전도 순서가 달라요 ! >

    > > a = { ' j a n ' : 1 , ' a p r ' : 4 } > > > b = { ' a p r ' : 4 , ' j a n ' : 1 } > > > p r i n t a , b { ' j a n ' : 1 , ' a p r ' : 4 } { ' a p r ' : 4 , ' j a n ' : 1 } > > > p r i n t a = = b T r u e > > > p r i n t a . i t e m s ( ) = = b . i t e m s ( ) F a l s e 46 / 73
  38. 48.

    리사이즈 중요성 키가 많아질 수록 충돌이 잦아진다 배열이 꽉 차면

    ? 영원히 빙빙 돌 수도 있다 ! 배열 크기의 2/3 보다 키가 많아지면 크기를 늘린다 기존 배열 크기의 2 배 혹은 4 배 48 / 73
  39. 49.

    영향 : 엄청 빠르다 배열의 1/3 이상은 항상 비어 있다

    만약 우리가 원하는 키가 없다면 : 0 번 헛수고할 확률 : 1/3 = 33% 1 번 헛수고할 확률 : 2/3 * 1/3 = 22% 2 번 헛수고할 확률 : 2/3 * 2/3 * 1/3 = 14% .. 10 번 헛수고할 확률 : 0.5% 49 / 73
  40. 50.

    영향 : 순서가 바뀜 a = { 8 : '

    h e l l o ' , 1 : ' w o r l d ' } b = { 8 : ' h e l l o ' , 1 : ' w o r l d ' } # 키 몇 개를 주르르 추가: 리사이징이 일어난다! f o r i i n x r a n g e ( 5 ) : b [ i + 1 0 0 ] = 1 # 지워도 이미 늘어난 배열은 아직 줄어들지 않았다 f o r i i n x r a n g e ( 5 ) : d e l b [ i + 1 0 0 ] p r i n t a = = b # T r u e p r i n t a . i t e m s ( ) = = b . i t e m s ( ) # F a l s e ! 50 / 73
  41. 51.

    영향 : 순서가 바뀜 A B 인덱스 키 값 0

    8 'hello' 1 1 'world' 2 (EMPTY) (EMPTY) 3 (EMPTY) (EMPTY) 4 (EMPTY) (EMPTY) 5 (EMPTY) (EMPTY) 6 (EMPTY) (EMPTY) 7 (EMPTY) (EMPTY) 인덱스 키 값 0 (EMPTY) (EMPTY) 1 1 'world' 2 (EMPTY) (EMPTY) 3 (EMPTY) (EMPTY) 4 (EMPTY) (EMPTY) 5 (EMPTY) (EMPTY) 6 (EMPTY) (EMPTY) 7 (EMPTY) (EMPTY) 8 8 'hello' .. .. .. 51 / 73
  42. 52.

    영향 : 순서가 바뀜 k e y s ( )

    와 v a l u e s ( ) 의 순서가 달라질 수 있는 이유 v a l u e s ( ) 를 호출하면 새 리스트를 만든다 이때 GC 호출이 되면 사전의 크기가 바뀔 수 있음 ! 52 / 73
  43. 53.

    영향 : 순회 중 업데이트 m o n t h

    s = { ' j a n ' : 1 , ' f e b ' : 2 , ' m a r ' : 3 , ' a p r ' : 4 , . . } f o r k , v i n m o n t h s . i t e r i t e m s ( ) : i f v > = 6 : d e l m o n t h s [ k ] p r i n t m o n t h s R u n t i m e E r r o r : d i c t i o n a r y c h a n g e d s i z e d u r i n g i t e r a t i o n 53 / 73
  44. 54.

    영향 : 순회 중 업데이트 m o n t h

    s = { ' j a n ' : 1 , ' f e b ' : 2 , ' m a r ' : 3 , ' a p r ' : 4 , . . } f o r k , v i n m o n t h s . i t e m s ( ) : i f v > = 6 : d e l m o n t h s [ k ] p r i n t m o n t h s { ' m a r ' : 3 , ' m a y ' : 5 , ' f e b ' : 2 , ' j a n ' : 1 , ' a p r ' : 4 } 54 / 73
  45. 55.

    영향 : 사용자 클래스 c l a s s U

    s e r ( o b j e c t ) : d e f _ _ i n i t _ _ ( s e l f , n a m e , e m a i l ) : s e l f . n a m e = n a m e s e l f . e m a i l = e m a i l > > > j o n g m a n = U s e r ( ' j o n g m a n ' , ' j o n g m a n @ g m a i l . c o m ' ) > > > r a t i n g = { j o n g m a n : ' g o o d ' } > > > p r i n t r a t i n g [ j o n g m a n ] g o o d 오 .. 잘 된다 ! 55 / 73
  46. 56.

    영향 : 사용자 클래스 과연 그럴까 ? > > >

    j o n g m a n 2 = U s e r ( ' j o n g m a n ' , ' j o n g m a n @ g m a i l . c o m ' ) > > > p r i n t r a t i n g [ j o n g m a n 2 ] K e y E r r o r : < _ _ m a i n _ _ . U s e r o b j e c t a t 0 x 1 0 5 e 1 c 9 1 0 > 기본적으로는 모든 인스턴스를 다른 키로 인식 ! 해시에 사용할 수 있도록 추가 구현이 필요 56 / 73
  47. 57.

    영향 : 사용자 클래스 두 가지 메소드 구현 : _

    _ h a s h _ _ ( s e l f ) : 해시값을 반환 _ _ e q _ _ ( s e l f , o t h e r ) : 두 값이 같은지 반환 ( 충 돌 해결에 사용 ) 어떻게 ? 멤버 변수들이 모두 같으면(*) 같은 값 ! 57 / 73
  48. 58.

    영향 : 사용자 클래스 c l a s s U

    s e r ( o b j e c t ) : . . d e f m e m b e r s ( s e l f ) : r e t u r n ( s e l f . n a m e , s e l f . e m a i l ) d e f _ _ e q _ _ ( s e l f , o t h e r ) : r e t u r n s e l f . m e m b e r s ( ) = = o t h e r . m e m b e r s ( ) d e f _ _ h a s h _ _ ( s e l f ) : r e t u r n h a s h ( s e l f . m e m b e r s ( ) ) 58 / 73
  49. 61.

    dict 스러운 클래스들 c o l l e c t

    i o n s . O r d e r e d D i c t c o l l e c t i o n s . d e f a u l t d i c t c o l l e c t i o n s . C o u n t e r s h e l v e . S h e l f 61 / 73
  50. 62.

    OrderedDict 삽입한 순서대로 순회가 이루어진다 ! > > > f

    r o m c o l l e c t i o n s i m p o r t O r d e r e d D i c t > > > d = O r d e r e d D i c t ( ) > > > d [ ' g i r l s ' ] = 1 > > > d [ ' g e n e r a t i o n ' ] = 2 > > > d [ ' g g g g ' ] = 3 > > > d [ ' b a b y b a b y ' ] = 4 > > > d . i t e m s ( ) [ ( ' g i r l s ' , 1 ) , ( ' g e n e r a t i o n ' , 2 ) , ( ' g g g g ' , 3 ) , ( ' b a b y b a b y ' , 4 ) ] 62 / 73
  51. 63.

    OrderedDict 이렇게 하면 실패 # 이렇게 하면 한줄에 할 수

    있겠지? 히히 > > > d = O r d e r e d D i c t ( g i r l s = 1 , g e n e r a t i o n = 2 , g g g g = 3 , b a b y b a b y = 4 ) # 이런. . > > > d . i t e m s ( ) [ ( ' b a b y b a b y ' , 4 ) , ( ' g g g g ' , 3 ) , ( ' g e n e r a t i o n ' , 2 ) , ( ' g i r l s ' , 1 ) ] 63 / 73
  52. 64.

    defaultdict > > > f r o m c o

    l l e c t i o n s i m p o r t d e f a u l t d i c t > > > b y _ l e n = d e f a u l t d i c t ( l a m b d a : [ ] ) > > > f o r w i n w o r d s : b y _ l e n [ l e n ( w ) ] . a p p e n d ( w ) > > > b y _ l e n { 1 : [ ' A ' , ' a ' , ' B ' , ' b ' , ' C ' , . . . ] , 2 : [ ' a a ' , ' A b ' , ' a d ' , ' a e ' , ' A h ' , . . . ] , . . } s e t d e f a u l t 하는 것과 같은 효과 기본값이 아니라 기본값을 반환하는 함수 ! 64 / 73
  53. 65.

    defaultdict > > > f r o m c o

    l l e c t i o n s i m p o r t d e f a u l t d i c t > > > b y _ l e n = d e f a u l t d i c t ( l i s t ) > > > f o r w i n w o r d s : b y _ l e n [ l e n ( w ) ] . a p p e n d ( w ) > > > b y _ l e n { 1 : [ ' A ' , ' a ' , ' B ' , ' b ' , ' C ' , . . . ] , 2 : [ ' a a ' , ' A b ' , ' a d ' , ' a e ' , ' A h ' , . . . ] , . . } 대부분 타입명도 함수 (callable) 65 / 73
  54. 66.

    nested defaultdicts 각 길이마다 첫 글자별로 모아 보자 ! A

    [ 4 ] [ ' h ' ] = [ ' h a a b ' , ' h a a f ' , ' h a b u ' , ' h a c k ' , . . ] A [ 5 ] [ ' h ' ] = [ ' h a b i t ' , ' h a c h e ' , ' h a c k y ' , ' h a d d o ' , . . ] A = d e f a u l t d i c t ( l a m b d a : d e f a u l t d i c t ( l i s t ) ) f o r w i n w o r d s : A [ l e n ( w ) ] [ w [ 0 ] ] . a p p e n d ( w ) 66 / 73
  55. 67.

    infinite defaultdicts > > > i n f i n

    i t e _ d i c t = l a m b d a : d e f a u l t d i c t ( i n f i n i t e _ d i c t ) > > > i n f = i n f i n i t e _ d i c t ( ) > > > i n f [ ' U s e r s ' ] [ 0 ] [ ' u s e r n a m e ' ] = ' j o n g m a n ' > > > i n f [ ' U s e r s ' ] [ 0 ] [ ' e m a i l ' ] = ' j o n g m a n @ g m a i l . c o m ' > > > i n f . i t e m s ( ) [ ( ' U s e r s ' , d e f a u l t d i c t ( < f u n c t i o n < l a m b d a > a t 0 x 1 0 2 5 d 0 9 3 8 > , { 0 : d e f a u l t d i c t ( < f u n c t i o n < l a m b d a > a t 0 x 1 0 2 5 d 0 9 3 8 > , { ' u s e r n a m e ' : ' j o n g m a n ' , ' e m a i l ' : ' j o n g m a n @ g m a i l . c o m ' } ) } ) ) ] 67 / 73
  56. 68.

    Counter > > > f r o m c o

    l l e c t i o n s i m p o r t C o u n t e r > > > l e n g t h _ c o u n t = C o u n t e r ( m a p ( l e n , w o r d s ) ) # d i c t 처럼 원소에 접근 > > > p r i n t l e n g t h _ c o u n t [ 1 ] , l e n g t h _ c o u n t [ 2 ] , l e n g t h _ c o u n t [ 3 ] 5 2 1 6 0 1 4 2 0 # 없는 키는 0 을 반환 > > > p r i n t l e n g t h _ c o u n t [ 1 2 3 7 8 1 2 ] 0 # 가장 흔한 길이 > > > p r i n t l e n g t h _ c o u n t . m o s t _ c o m m o n ( 3 ) [ ( 9 , 3 2 4 0 3 ) , ( 1 0 , 3 0 8 7 8 ) , ( 8 , 2 9 9 8 9 ) ] 68 / 73
  57. 69.

    shelve.Shelf f r o m s h e l v

    e i m p o r t o p e n s h e l f = o p e n ( ' t e s t ' ) # t e s t . d b 에 사전 내용을 기록 s h e l f [ ' h e l l o ' ] = 1 s h e l f [ ' w o r l d ' ] = 2 s h e l f . c l o s e ( ) s h e l f = o p e n ( ' t e s t ' ) # 이미 있는 t e s t . d b 를 읽어옴 p r i n t s h e l f . i t e m s ( ) [ ( ' h e l l o ' , 1 ) , ( ' w o r l d ' , 2 ) ] 69 / 73
  58. 70.

    shelve.Shelf: 주의 문자열 키만 가능 c l o s e

    ( ) 를 빼먹으면 저장이 안됨 context manager! f r o m s h e l v e i m p o r t o p e n f r o m c o n t e x t l i b i m p o r t c l o s i n g w i t h c l o s i n g ( o p e n ( ' a . s h e l f ' ) ) a s s h e l f : s h e l f [ ' h e l l o ' ] = 1 s h e l f [ ' w o r l d ' ] = 2 70 / 73
  59. 71.

    Resources PyCon 2013: Raymond Hettinger PyCon 2010: The Mighty Dictionary

    CPython 소스 코드 : dictnotes.txt, dictobject.c Module of the Week: defaultdict, shelve, OrderedDict, Counter Beautiful Code, 18 장 71 / 73