functools에 있는 python partial 함수는 어떤 일을 할까요? 이 글에서 간단하게 알아봅시다.
함수에 대한 설명
먼저, 설명을 보고 간단한 예제를 작성해 보도록 하겠습니다.
이 함수는 아래와 같은 인자를 받습니다.
- __func
- *args, **kwargs
- 인자와 키워드 인자들
어떤 것을 돌려주는가? 새로운 함수를 돌려주는데, 넘겨준 arguments와 keywords를 넘겨받은 것을 돌려줍니다. 예제를 하나 보도록 하겠습니다.
foo 함수는 key와 val 인자를 받습니다. 8번째 줄에, python partial 함수를 적용한 결과를 받습니다. 2번째 인자로 val=3을 넘겨주었는데요. 이는 base_foo 에는 인자 val이 3으로 들어간다는 의미입니다. 여기까지 상황을 그려보도록 하겠습니다.
val이 3이라는 정보가 base_foo에 들어갑니다. 이렇게 되면, base_foo에는 key 값만 넘겨주면 됩니다. 9번째 줄에는 key의 값으로 “foo”를 넘겨주었습니다. 이미 base_foo에는 호출할 foo 함수가 인자 val의 값이 3이라는 정보가 있었습니다.
이 정보에, va따라서, base_foo(key=”foo”)를 호출하면, 호출할 함수 foo는 인자 2개를 받는데, key의 값이 “foo”이고, val 값이 3이라는 정보가 완성됩니다. 따라서, 문자열 “foo = 3″이 출력됩니다.
9번째 줄의 base_foo(key=”bar”)가 호출되면 어떤 일이 벌어질까요? foo 함수가 호출되는데, 인자 key의 값이 “bar”로, val의 값이 3으로 넘겨지게 됩니다. 따라서, 문자열 “bar = 3″이 출력됩니다. 예제 1번의 결과는 아래와 같습니다.
이렇게 partial 함수는 넘겨주는 인자의 값을 고정 시키기 위해 사용할 수 있습니다.
python partial 함수의 다른 예제
defaultdict 클래스는 __getitem__ 에서 키가 없을 때 키를 생성하기 위한 factory를 넘겨줄 수 있습니다. 자세한 사항은 이 글을 참고하세요.
이 글에서는 이를 재귀적으로 구현해 보도록 합니다. 먼저, __getitem__에서 key가 없을 때 0을 리턴하게 하는 defaultdict는 defaultdict(int)를 해 주면 됩니다. depth가 1인 경우입니다. 2인 경우에는 어떻게 해야 할까요? 예제 3번과 같이 하면 됩니다.
이 프로그램은 아래와 같이 동작합니다.
- 키 3이 없으므로, f가 호출됩니다.
- defaultdict에서, 키 2가 없으므로 int가 호출됩니다.
depth가 3인 경우에는 어떻게 해야 할까요?
예제 4번과 같이 하면 됩니다. 이 프로그램이 어떻게 동작하는가? 마찬가지로 해석하시면 됩니다.
- dic에서 3이 없기 때문에, g가 호출됩니다.
- g는 키가 없을 때 f를 호출하는 defaultdict를 리턴합니다.
- dic[3]에서 키 2가 없으므로, f가 호출됩니다.
- dic[3][2]는 defaultdict(int)와 연결됩니다.
- dic[3][2]에 키 4가 없기 때문에, int가 호출됩니다.
뭔가 복잡한데요. 그림으로 도식화 시키면 아래와 같습니다.
먼저 dic = defaultdict(g)라고 했어요. 3이 dic에 없으면 g가 호출됩니다. 그리고, defaultdict(f)가 리턴될 겁니다. dic[3] = defaultdict(f)가 들어갑니다.
다음에, dic[3]에 키 2가 없습니다. dic[3]에는 factory가 f인 디폴트 딕셔너리가 있었습니다. 3은 있었고, 2가 없습니다. defaultdict(f) 때문에, f가 호출됩니다. f는 factory가 int인 디폴트 딕셔너리를 돌려줍니다.
이제 dic[3][2]에 4가 있는지 볼까요? 없어요. dic[3][2]는 있기 때문에, defaultdict(int)를 가져 옵니다. factory가 int인 경우, 0을 돌려줍니다. 예제 5번에서 함수 g를 보면, return defaultdict(f)와 같이 되어 있습니다. 이를 partial로 변환하면 아래와 같이 됩니다.
- conv = partial(defaultdict, conv)
이는 1번째 인자가 conv인 defaultdict 함수를 돌려달라는 의미입니다. 이제 아래 6번 예제를 해석해 봅시다.
먼저, 5번째 줄의 conv는 아래를 의미합니다.
- 함수가 defauldict이고 인자 default_factory가 int인 것.
이 상태에서 dic = conv()를 호출하면, dic는 디폴트 factory가 int인 default_dictionary가 될 겁니다. 그런데, 6번째 줄과 7번째 줄에 아래와 같은 문장이 있습니다.
- conv = partial(defaultdict, conv)
이 말이 무슨 의미일까요?
__getitem__ 함수에서 __missing__ 이벤트가 발생하였을 때, conv를 호출해서 값을 할당한다는 의미입니다. 따라서, 6번째 줄의 conv와 7번째 줄의 conv는 아래와 같음을 알 수 있습니다.
- 5번째 줄의 conv는 defaultdict(int), f와 같습니다.
- 6번째 줄의 conv는 defaultdict(f), g와 같습니다.
- 7번째 줄의 conv는 defaultdict(g)와 같습니다.
중요한 것은 default_factory가 재귀적으로 정의되었다는 것입니다.