달나라 노트

Python Basic : *args, ** kwargs (무작위 개수의 parameter, arguments, keyword arguments) 본문

Python/Python Basic

Python Basic : *args, ** kwargs (무작위 개수의 parameter, arguments, keyword arguments)

CosmosProject 2022. 9. 28. 20:12
728x90
반응형

 

 

 

Python을 다루다보면 아래와 같은 함수를 볼 수 있습니다.

 

def test_1(*args):
    ...


def test_2(**kwargs):
    ...

 

함수의 내용은 중요한게 아니라서 ...으로 생략하여 표시하였습니다.

중요한건 함수의 parameter 부분입니다.

 

보통 함수에서 parameter를 설정할 때 특정 키워드를 괄호안에 넣어두죠.

근데 위 함수를 보면 *(asterisk)를 이용하여 *args, **kwargs 라는 이상한 parameter를 넣어놨습니다.

 

이것은 당연히 일반적인 parameter와는 조금 다른 기능을 가지며 저것들이 대체 무엇을 의미하고 어떤식으로 사용할 수 있는지를 알아봅시다.

 

 

 

일단 상황을 한번 가정해봅시다.

 

여러분이 여러 개의 값들을 사용자로부터 input받아서 이 값들을 차례대로 출력해주는 함수를 만든다고 합시다. (이게 실제로 효용성이 있을지는 모르겠지만 그냥 그렇다고 합시다.)

 

def test_1(values):
    print(values)

 

사용자에게 input을 받는 부분은 일단 제쳐두고 어떤 값을 출력하는 함수를 만들라면 간단하게 위처럼 만들 수 있습니다.

근데 여기서 한가지 문제가 있습니다.

사용자가 몇개의 값을 입력할까요?

 

모르죠. 사용자마다 다를겁니다.

사용자로부터 받은 여러 개의 값을 출력해주는 기능이 필요한 것이지 사용자로부터 몇개의 값을 받아서 출력해준다고는 안했습니다.

즉, 사용자가 input할 값의 개수도 모르며 그에 따라 함수에 들어갈 parameter의 개수가 몇개인지도 모릅니다.

1개일 수도 있고 100개일 수도 있습니다.

 

근데 위 함수는 parameter로서 딱 1개의 값을 받아야만 하는 함수입니다.

문제가 있죠.

 

게다가 일반적인 함수는 parameter의 개수가 정해져있습니다.

그렇기에 일반적인 방법으로는 parameter가 몇개가 주어질지 모르는 상황에서의 함수는 작성할 수 없죠.

 

이러한 경우에 *args를 사용합니다.

 

def test_1(*args):
    print(type(args))
    print(args)
    for i in args:
        print(i)

test_1(1, 2, 3, 4, 5, 6, 7, 8)



-- Result
<class 'tuple'>
(1, 2, 3, 4, 5, 6, 7, 8)
1
2
3
4
5
6
7
8

 

위 예시를 보면 함수의 parameter로서 *args를 넣어뒀습니다.

args는 arguments의 약자이고, 그냥 Python에서 대표적으로 쓰는 이름입니다.

중요한건 parameter에 *(asterisk)가 붙었다는 것입니다.

함수의 parameter 앞에 * 표시가 1개 붙으면 전달받은 모든 argument들을 하나의 tuple로 묶어서 전달하겠다는 의미입니다.

이 의미가 뭔지 아래에서 자세히 살펴봅시다.

 

 

test_1 함수를 선언하는 부분에선 분명히 parameter가 *args 딱 하나만 있습니다.

근데 test_1 함수 호출 부분을 보면 test_1(1, 2, 3, 4, 5, 6, 7, 8)로 총 8개의 parameter를 전달하고있죠.

 

이렇게 * 표시와 함께 선언된 함수의 parameter는 함수 호출 시 전달된 모든 값을 하나의 tuple 로 묶어서 받습니다.

 

그래서 함수 내부에 args를 출력한 부분을 보면 type이 tuple이고 (1, 2, 3, 4, 5, 6, 7, 8)이 출력된 것을 볼 수 있죠.

 

그리고 for loop를 이용해서 이 tuple 속의 값을 하나씩 출력하고 있습니다.

 

 

 

 

 

 

def test_1(*args):
    print(type(args))
    print(args)
    for i in args:
        print(i)

test_1(1, 2, 3, 4, 5)



-- Result
<class 'tuple'>
(1, 2, 3, 4, 5)
1
2
3
4
5

 

동일한 예시에서 함수 호출 시 1, 2, 3, 4, 5의 5개 인자만 전달해보았습니다.

이번에도 마찬가지로 전달받은 5개의 인자를 묶어서 tuple의 형태로 만드는 것을 볼 수 있습니다.

 

즉, 함수 호출 시 전달되는 parameter의 개수가 몇개건 상관 없이 전달받은 모든 인자를 tuple로 묶는다는 의미이죠.

 

 

여기서 한 가지 더 주의할 것은 함수 선언 시 parameter를 *args로 적었는데 중요한 것은 * 기호이지 args가 아닙니다.

무슨 말이냐면 args는 그냥 parameter의 이름일 뿐입니다.

전달받은 인자의 개수에 상관없이 모든 인자를 tuple로 묶어서 함수를 진행하겠다 라는 의미는 *(asterisk)가 내포하고 있습니다.

 

즉, *args에서 args는 변해도 됩니다. 원하는 이름을 쓰면 돼요.

 

def test_1(*muliple_values):
    print(type(muliple_values))
    print(muliple_values)
    for i in muliple_values:
        print(i)

test_1(1, 2, 3, 4, 5)



-- Result
<class 'tuple'>
(1, 2, 3, 4, 5)
1
2
3
4
5

 

위 예시를 보면 *args를 *multiple_values로 바꿨습니다.

함수 내에서의 참조도 모두 args가 아니라 multiple_values로 참조하고있구요.

parameter의 이름은 바뀌었지만 기능은 동일합니다.

 

다시 정리해보면 *args의 의미는 함수 호출 시 전달받은 모든 인자를 tuple의 형태로 묶어서 args라는 이름의 변수에 할당하여 함수 내에서 사용할 수 있도록 해라 라는 의미입니다.

 

*multiple_values도 동일합니다.

함수 호출 시 전달받은 모든 인자를 tuple의 형태로 묶어서 multiple_values라는 이름의 변수에 할당하여 함수 내에서 사용할 수 있도록 해라 라는 의미입니다.

 

 

 

 

 

 

 

이제 **kwargs를 알아봅시다.

**kwargs도 거의 비슷한 의미입니다만, * 표시가 2개 있는 것으로 보아 뭔가 좀 다른 것을 짐작할 수 있죠.

 

* 표시가 1개 붙은 경우 (*args, *values 등)는 주어진 모든 인자를 tuple로 묶어서 다룬다는 의미였다면

** 표시가 2개 붙은 경우 (**kwargs, **values 등)는 주어진 모든 인자를 dictionary의 형태로 묶어서 다룬다는 의미입니다.

 

def test_2(**kwargs):
    print(type(kwargs))
    print(kwargs)

    for k, v in kwargs.items():
        print(k, v)


test_2(first=1, second=2, third=3)



-- Result
<class 'dict'>
{'first': 1, 'second': 2, 'third': 3}
first 1
second 2
third 3

 

위 예시를 봅시다.

test_2 함수의 인자로서 **kwargs가 존재합니다.

 

kwargs는 keyword arguments의 약자로 keyword가 있는 형태의 값으로 묶어서 사용하겠다는 것입니다. 즉, dictionary 형태로 묶어서 사용하겠다는 것이죠.

 

test_2 함수의 호출 부분을 보면 인자가 다음과 같이 적혀있습니다.

first=1, second=2, third=3

 

2개의 * 표시가 붙은 인자를 가지고 있는 함수의 경우 함수를 호출할 때 반드시 위같은 형식으로 인자를 전달해야합니다.

그러면 등호(=) 왼쪽에 있는 first, second, third 값이 dictionary의 key가 되고,

등호(=) 오른쪽에 있는 1, 2, 3 값이 dictionary의 value가 됩니다.

 

위 예시에서 함수 내부에서 kwargs를 출력한 부분을 보면 다음과 같습니다.

<class 'dict'>
{'first': 1, 'second': 2, 'third': 3}

 

type이 dictionary이고 그 dictionary의 key, value 쌍이 함수 호출 시 인자로 전달됐던 first=1, second=2, third=3 이것과 동일하게 구성된 것을 알 수 있죠.

 

따라서 함수 내부에서 kwargs를 사용할 때에도 일반 dictionary처럼 사용하면 됩니다.

위 예시에서는 for loop와 dictionary의 item method를 이용하여 dictionary의 모든 요소를 출력해보았습니다.

 

 

 

 

**kwargs도 *args와 마찬가지로 **라는 표시가 중요한거지 kwargs라는 문자가 중요한게 아닙니다.

kwargs라는 문자는 단순히 함수의 parameter 이름을 의미하고 **라는 표시가 바로 key, value 쌍으로 된 인자를 개수제한 없이 받아서 하나의 dictionary로 묶어 하나의 변수에 할당하겠다는 의미입니다.

 

def test_2(**new_value):
    print(type(new_value))
    print(new_value)

    for k, v in new_value.items():
        print(k, v)


test_2(first=1, second=2, third=3)



-- Result
<class 'dict'>
{'first': 1, 'second': 2, 'third': 3}
first 1
second 2
third 3

 

그래서 위처럼 **kwargs를 **new_value로 바꿔도 결과는 동일합니다.

 

 

 

 

 

 

- 추가

 

위 내용을 읽었을 때 python 함수에 전달할 수 있는 인자는 총 3가지가 있습니다.

1. 일반적인 parameter

2. *args

3. **kwargs

 

그러면 함수는 위 세개의 parameter 중 하나만 명시할 수 있을까요?

아닙니다.

함수를 생성할 때 위 3개를 동시에 명시할 수도, 3개 중 2개 종류의 parameter만 명시할 수도 있습니다.

 

def test_1(x, y, *args, **kwargs):
    ...

 

예를들어 위처럼

일반 parameter인 x, y와 *args, **kwargs 를 동시에 사용할 수도 있습니다.

 

근데 여기서는 한 가지 주의할 점이 있습니다.

명시할 parameter의 개수는 중요하지 않지만 이 변수들의 순서입니다.

 

1. 일반적인 parameter

2. *args

3. **kwargs

 

 

 

이 세 개 종류의 변수를 사용할 때에는 반드시 아래와 같은 순서로 적어주어야 합니다.

 

일반적인 parameter -> *args -> **kwargs

 

함수의 parameter를 적을 때 일반적인 parameter를 가장 왼쪽에 쓰고, 그 다음 *args, 그 다음 **kwargs를 적어주어야 한다는 의미입니다.

 

def test_1(*args, **kwargs, x, y):
    ...

 

위처럼 *args, **kwargs보다 더 나중에 일반 변수인 x, y를 명시하면 안됩니다.

 

 

 

 

def test_1(x, y, *args, **kwargs):
    ...

test_1(1, 2, 3, 4, 5, arg1='a', arg2='b)

 

그 이유는 아래와 같습니다. 한가지 예시를 들어보죠.

test_1 함수를 실행할 때 인자를 위처럼 전달할 겁니다.

위 인자를 연결해보면 다음과 같습니다.

 

x = 1

y = 2

*args = (3, 4, 5)

**kwargs = {'arg1': 'a', 'arg2': 'b'}

 

 

 

def test_1(*args, **kwargs, x, y):
    ...

test_1(1, 2, 3, 4, 5, arg1='a', arg2='b)

 

근데 만약 함수의 parameter를 위같은 순서로 적어두었다면 어떻게 될까요?

 

*args = ?

**kwargs = ?

x = ?

y = ?

 

각 인자가 어떤 값을 받게될지 알 수 있나요?

아뇨. 알 수 없습니다.

 

*args, **kwargs라는 것 자체가 수백 수천개의 인자를 전달받아도 하나로 묶어서 다루는 기능을 가지고 있는데,

함수에서 *args, **kwargs를 먼저 명시해두고 일반 변수를 가장 오른쪽에 명시하면, 대체 어디서부터 어디까지가 *args, **kwargs에 할당되는지 모르기 때문입니다.

 

그래서 추후 여러 종류의 parameter를 사용할 땐 위 내용을 참고하여 사용하면 좋습니다.

 

 

 

 

 

 

728x90
반응형
Comments