*args와 **kwargs

*args

입력받은 숫자들을 더해주는 함수를 만든다고 가정해보자.
만약 입력받은 숫자가 2개라면 아래와 같이 함수를 만들면 된다.

def sum_two(a, b):  
    return a + b

sum_two(3, 5) # 8  

마찬가지로 입력받은 숫자가 3개라면,

def sum_three(a, b, c):  
    return a + b + c

sum_three(3, 5, 7) # 15  

하지만 입력받는 숫자가 4개, 5개,... 계속 늘어날 때마다 함수를 새로 짤 수 없는 노릇이고, 또 입력받는 갯수를 알 수 없을 때는 함수를 구현하기가 불가능하다. 물론 list를 이용하면 구현할 수도 있지만 여기서는 다루지 않겠다.
이 때, 함수의 parameter로 *args를 이용하면 argument들의 수에 구애받지 않는 매력적인 함수를 만들 수 있다.

def sum_args(*args):  
    sum= 0
    for arg in args:
        sum+= arg
    return sum

sum_args(1, 2, 3, 4) # 10  

이 함수는 parameter로 위와 같이 1, 2, 3, 4 라고만 넣어줘도 잘 작동한다. 이 원리는 과연 무엇일까?
다음과 같이 tuple 형태의 자료형을 넣어보면 더 잘 이해할 수 있다.

values = (1,2,3,4)  
sum_args(values) # 에러  
sum_args(*values) # 10  

2번째 줄의 코드는 에러가 뜰 것이고, 3번째 줄의 코드는 위의 경우처럼 잘 작동할 것이다. 그 이유는 앞에 붙은 *의 역할에서 찾을 수 있다. 앞에 *을 붙이면 tuple이나 list 형태의 자료를 unpacking하여 반환한다.
unpacking이란 말그대로 포장을 벗겨내는 것, 즉 쉽게 말해 tuple이나 list에서의 괄호나 대괄호를 벗겨낸다고 생각하면 된다. 따라서 3번째 줄의 코드에서 *value = 1, 2, 3, 4가 되며 이는 tuple이나 list 형태의 자료형이 아니다.
이 값이 다시 sumargs 함수의 parameter로 들어가게 되면 *args = 1, 2, 3, 4가 된다. 이 말은 거꾸로 args = (1, 2, 3, 4)가 되며(이 반대의 과정을 *packing*이라 한다.) for 문에서 문제없이 작동하게 된다.
하지만 2번째 줄의 코드에서는 sum
args 함수의 parameter로 tuple 형태의 자료형인 (1, 2, 3, 4)가 그대로 들어가게 되며 *args = (1, 2, 3, 4)가 된다. *args에는 unpacking된 값이 들어가야 되는데, packing된 값이 들어갔으므로 에러가 뜬다.
이를 좀 더 활용하면 아래와 같이 코드를 짤 수도 있다.

def myprofile_two(name, age):  
    print("{name}님의 나이는 {age}입니다.".format(name=name, age=age))

profile_tuple = ("홍길동", 25)  
myprofile_two(*profile_tuple) # 홍길동님의 나이는 25입니다.  

**kwargs

*args가 확실히 이해됐다면 **kwargs도 쉽게 이해될 것이다.
먼저 dictionary형의 데이터를 하나 만들고,

profile_dict= {  
    "name" : "홍길동",
    "age" : 25,
}

아까 위에서 만든 함수를 그대로 사용하여 출력하면

print(myprofile_two(*profile_dict))  

name님의 나이는 age입니다.

라는 결과를 얻을 수 있다.
이와 같은 결과가 발생하는 이유는 쉽게 생각해서 *를 사용하면 포장의 껍질을 한번만 벗겨낼 수 있기 때문이다. 따라서 dictionary의 value 값은 가져오지 못하고, key 값만 argument로 넘겨준다.
우리가 원하는 결과는 value 값들의 출력이므로, 이 때는 **를 앞에 붙이면 된다.

myprofile_two(**profile_dict) # 홍길동님의 나이는 25입니다.  

결과적으로 정리하면, *은 list나 tuple을 unpacking 해주는 역할을 하고, **은 dictionary을 unpacking 해준다.
방금은 함수에서 name과 age라는 고정된 2개의 arguments만 받았지만, **kwargs를 이용하면 함수를 만들면 다음과 같이 argument의 수와 관계없이 처리하는 동적인 함수도 만들 수 있다.

profile_dict_many= {  
    "name" : "홍길동",
    "age" : 25,
    "height" : 175,
    "country" : "대한민국",
}

def myprofile_kwargs(**kwargs):  
    for key,value in kwargs.items():
        print("{key} = {value}".format(key=key,value=value))

myprofile_kwargs(**profile_dict_many)