python defaultdict 클래스는 dict와 거의 비슷합니다. 다른 것은 키가 없을 때 default_factory로 값을 설정할 수 있다는 것입니다. 저는 이를 어떤 용도로 주로 쓰는가? 딕셔너리의 딕셔너리를 선언할 때? [] 연산에서 키가 없는 경우 처리를 하기 위해 자주 씁니다. 제가 출제한 이 문제에서도 이용할 수 있으니 이번 기회에 알아가도록 합시다.
dictionary의 getitem 함수
우리가 파이썬에서 딕셔너리를 언제 이용하나요? key를 빠르게 찾을 때 이용해요. 예를 들어 아래의 코드가 있다고 해 봅시다.
2번째 줄에 dic[1]을 호출했어요. key 1에 대응되는 값을 얻어온다는 의미에요. 이 코드를 호출하면 함수 __getitem__ 이 호출됩니다. [] 을 호출했을 때, __getitem__이 호출됩니다.
그런데, 만약에 dic[2]를 호출하면 어떻게 될까요?
KeyError가 뜹니다. 이는 키가 없기 때문입니다. 여기까지 __getitem__ 함수의 흐름을 정리해 보겠습니다. 이 함수는 2가지 케이스로 나누어 집니다.
- key가 있을 때
- key가 없을 때
key가 있다면, 대응되는 값을 얻어옵니다. 그렇지 않으면 missing key이기 때문에 에러를 리턴합니다.
이 흐름이 대강이라도 이해되셨다면 다음으로 넘어갑시다.
python defaultdict 클래스와 __missing__
이제, __missing__ 으로 넘어가 보겠습니다. 이 함수는 __getitem__ 에서만 호출된다고 문서에 나와 있습니다. 이 말인 즉슨, get 함수를 호출해도, __missing__ 함수가 호출되지 않는다는 것입니다. python defaultdict 클래스 get 함수를 호출했을 때와, __getitem__을 호출했을 때 순서도를 그려 보도록 하겠습니다.
먼저 get 함수를 호출했을 때 순서도입니다. 키가 없는 경우, 그냥 missing key 이벤트만 발생합니다. 평소처럼 None을 리턴합니다. 왜? __getitem__ 에서만 함수 __missing__이 호출되기 때문입니다. 반면, __getitem(self, key)는 어떨까요?
그러면 __missing__(key)가 걸리면 어떻게 되는 건지 봐야 합니다. 이 때에는 다음과 같은 일이 일어납니다.
- default_factory를 호출합니다.
- 키가 없을 때 default 값을 생성해서 돌려줍니다.
- 이 값을 value 라고 했을 때, key에 대응되는 값은 value라는 정보를 넣습니다.
무슨 이야기인지 예제 프로그램을 보면서 이야기 해 봅시다.
factory 함수는 위와 같아요. “factory” 라는 문구를 출력하고 새로운 리스트를 돌려줍니다.
본체를 봅시다. defaultdict 생성자에 factory를 넣었어요. 이는, default 값을 생성하는 factory로 함수 factory를 넣는다는 의미입니다. 그냥 새로운 리스트 생성입니다. 다음 10번째 줄에 de[1].append(3)이 들어갔군요.
빈 defaultdict에는 키 1이 있을 리가 없으니, __missing__ 이 호출됩니다. 여기서 factory를 부르는데요. 제가 넘겨준 것은 빈 리스트였어요. 따라서, 키 1에 대응되는 값은 빈 리스트라는 정보가 딕셔너리에 추가됩니다.
다음에 append(3)이 실행됩니다. 그러면, 1에 대응되는 값인 빈 리스트에 3이 추가될 겁니다.
여기까지 상황이 이해 가시나요? 다음에 var = de[2]가 수행되고 나면, 아래와 같이 변할 겁니다.
de[2]를 호출했기 때문에 key 2에 대응되는 값은 빈 리스트라는 정보가 추가된 것입니다. 프로그램의 실행 결과를 볼까요?
factory가 2번 출력 되었습니다. 그리고 1에 대응되는 값은 [3], 2에 대응되는 값은 빈 리스트임을 알 수 있습니다. 이제, default factory의 역할을 다시 정리해 봅시다.
- 사용자가 dic[k]를 호출합니다.
- default dictionary에 키 값이 k인 것이 없었다면
- default factory로 값 v를 만듭니다.
- 키 k에 대응되는 값은 v라는 정보를 dic에 추가합니다.
결국 키 key가 없을 때, key에 대응되는 값이 디폴트 값이라는 정보를 넣기 위해, 디폴트 값을 생성해 주는 것이 default factory의 역할이라 할 수 있겠습니다.
또 다른 예제
그러면, 아래는 무엇을 의미할까요?
default factory로 dict가 들어갔어요. 이 말은 무슨 이야기인가? 5번째 줄에서 de[1][3] = 1 문장이 있어요.
- de[1] 에서 __getitem__(self, 1)을 호출해요.
- 1이 없다면 default factory를 호출하는데 이게 마침 dict네요.
- 키가 없으면 딕셔너리를 만든다는 의미입니다. 이를 v라 합시다.
- 키 1에 대응되는 값은 v라는 정보를 de에 넣어요.
여기까지 수행해 봅시다. 그러면, 아래 그림과 같이 됩니다.
이제 de[1][3] = 1을 해석해 봅시다. de[1]에 대응되는 것은 그냥 딕셔너리에요. 이 딕셔너리에, key값이 3이고 value가 1이라는 정보를 넣어요. 그러면 어떻게 될까요?
요래 그려지게 됩니다. 즉, defaultdict 안에 dictionary가 들어가게 된 셈입니다. 이 구조를 잘 이용하면 제가 출제한 문제를 풀 수 있습니다.