ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [python] **kwargs가 뭐지? 영상으로 설명
    Python 2022. 3. 27. 11:57

    https://github.com/shenweichen/DeepCTR-Torch/blob/master/deepctr_torch/callbacks.py

    (한숨이 절로 나는 **)

    들어가며:

    다른 사람들의 github를 보다가 **kwargs가 함수에 붙어있으면 (더블 포인터처럼 생겨서는) 이게 무슨 뜻인가 싶습니다. 

    C 프로그래밍을 하시면서 * 달려있으면 현기증 났었고 ** 달려있으면 코딩하는 걸 포기해버린 분들도 있으실 겁니다.

    파이썬에서는 다행히 주소값을 직접 건드릴 만큼 low-level로 프로그래밍을 하는 경우는 거의 없고, C보다는 **의 사용이 더 직관적입니다.

    다른 사람들의 github 코드들 보면 함수에 간간히 등장을 하는데, 이것을 어떻게 사용할까요? 그리고,

    왜 헷갈렸던 걸까요?

    (영상 설명은 본 포스트 마지막을 참고해주세요)

     

    **kwargs? :

    일단 kwargs는 keyword arguments의 줄임말입니다.

    프로그래밍에서 argument라는 것은 함수 입력을 말합니다. 혹은 함수 인자라고도 말합니다.

     

    argument는 함수 입력인데, keyword의 함수 입력이라는 말은 뭘까요?

     

    keyword는 직관적으로 의미를 갖는 단어, .. 그 언저리의 의미로 이해를 하는데, 여기서는 인자의 이름을 명백하게 명시하는 경우를 뜻합니다.

     

    **kwargs는 두 가지 경우에 사용을 합니다.

    1) 함수를 사용할 때 / 2) 함수를 선언할 때

     

    두 경우가 함수로 연결되어 긴밀한 듯 하지만 다른 원리로 작동합니다.

    그렇기 때문에 정확히 알아두지 않으면 사용에 헷갈릴 수 있습니다.

     

    1) 함수를 "사용"할 때

    아래 코드를 해석해볼까요?

    def func(a, b, c):
        return 3*a + 2*b + c
    
    out1 = func(3, 2, 1)
    out2 = func(a=3, b=2, c=1)
    
    k = {'a': 3, 'b': 2, 'c': 1}
    out3 = func(**k)
    
    k2 = {'a': 3, 'b': 2, 'c': 1, 'd': 4}
    out4 = func(**k2) # Error!

    func이라는 함수를 선언하는데, a, b, c의 인자를 받아서 3*a + 2*b + c를 return하게 했습니다.

    그리고 아래의 [line 4, 5]는 이 함수에 3, 2, 1을 순서대로 대입하여 함수를 실행합니다.

    out1과 out2는 같은 값이 있을 것입니다. 9 + 4 + 1 = 14로요.

     

    [line 7, 8]은 어떨까요?

    k는 dictionary로, 'a', 'b', 'c' 문자열에 각각 3, 2, 1 값을 대응시킨 key-value pair가 있는 dict입니다.

    out3은 func을 수행하는데 방금 만든 dictionary인 k에다가 **를 붙였습니다.

    아마 직관적인 느낌으로 a=3, b=2, c=1을 대입한 것이라고 예상되실 겁니다. 실제로도 맞습니다.

    [line 8]은 [line 5]와 동일하게 동작하는 코드라고 보시면 됩니다.

    함수를 사용할 때, **는 dictionary 인자 앞에 붙으며,
    key를 입력 변수 이름, value를 입력 값으로 변환해서 넣습니다. 

     

    [line 10, 11]은 어떨까요?

    k2에는 k dict에 'd': 4 아이템이 추가되어 있습니다.

    여기에 **를 붙여서 func에 입력하면

    func(a=3, b=2, c=1, d=4)

    와 같습니다.

    하지만 func은 선언 단계에서 d라는 입력을 받겠다는 언급은 하지 않았으므로 에러를 발생시킵니다.

     

     

    1) 함수를 "선언"할 때

    아래 코드를 해석해볼까요?

    def func(**kwargs):
        return kwargs["abc"] - kwargs["xyz"]
        
    out1 = func(abc=7, xyz=2)
    out2 = func(abc=8, pqr=2, xyz=4)
    out3 = func(pqr=3) # Error!
    out4 = func(3, abc=10, xyz=1) # Error!

    이번 경우에는 함수를 선언할 때 **를 사용했습니다. 아주 무시무시하죠.

    함수 동작을 보면 kwargs의 "abc" 아이템에서 "xyz"아이템을 뺀 값을 return합니다.

    kwargs를 사용할 때 문자열로 된 아이템을 사용을 하는데요.

     

    이걸 보니 kwargs도 뭔가 dictionary와 관련된 것 같은 느낌이 듭니다.

     

    원리부터 설명하면, 입력 이름을 밝혀서 넣은 인자가 kwargs에 dictionary 형태로 들어갑니다.

    1)에서의 원리가 반대로 적용된 것이죠.

     

    이렇게 선언한 함수를 아래 out1, out2, out3에서 사용을 하는데, out3, out4의 func을 사용할 때만 에러가 발생합니다.

     

    out1 = func(abc=7, xyz=2)에서는

    kwargs는 {"abc": 7, "xyz": 2}인 dictionary입니다. 여기서는 return에 필요한 "abc", "xyz" key가 정확하게 들어있기 때문에 코드도 잘 동작하고 직관적입니다. 결과는 7-2 = 5가 return됩니다.

     

    out2 = func(abc=8, pqr=2, xyz=4)에서는

    kwargs는 {"abc": 8, "pqr": 2, "xyz": 4}인 dictionary입니다. 여기서는 return에 필요한 "abc", "xyz"가 들어있고, "pqr"은 사용은 되지 않지만, 있다고 에러가 발생할 일은 코드상 없기 때문에 에러 없이 동작합니다. 결과는 8-4=4가 return됩니다.

     

    out3 = func(pqr=3)에서는

    kwargs는 {"pqr": 3}인 dictionary입니다. 여기서는 return에 필요한 "abc", "xyz"가 없습니다. 그런데도 kwargs dictionary의 "abc", "xyz" 키에 접근을 시도하므로 [line 6] ▶ [line 2]에서 Keyerror를 발생시킵니다.

     

    out4 = func(3, abc=10, xyz=1)에서는

    kwargs에 3을 넣을 수가 없습니다. 왜냐하면 dictionary는 항상 key와 value의 pair로 존재하기 때문입니다. 3이 들어갈 key가 정해지지 않았기 때문에 [line 7] ▶ [line 1]에서 함수에 입력을 전달하는 과정에서 Error를 발생시킵니다.

     

    이렇게 파이썬에서 **kwargs는 두 가지 원리로 동작을 합니다.

    비슷한 듯 하면서 서로 반대편에서 대응되는 관계에 있기 때문에 처음에는 조금 헷갈립니다.

    게다가, 실제로 함수를 선언하고 사용할 때는 인자를 다양한 방식으로 사용하기 때문에 더욱 헷갈릴 수 있습니다.

     

    함수의 입력을 사용하는 여러 경우에 대한 설명은 아래 영상에서 더 자세하게 설명되어있으니 아래 영상을 참고해주세요.

     

    영상 설명:

    https://www.youtube.com/watch?v=eUEmOHrHga8 

     

    댓글

Designed by Tistory.