Dev/Python

[Python 기초] *args와 **kwargs 사용과 순서

sincerely10 2020. 6. 25. 11:43
반응형

이번 포스트에서는 python의 args와 kwargs의 역할과 문법(순서)를 알아보겠습니다.

포스트 항목은 다음과 같습니다.
1. 사용 문법
2. args
3. kwargs
4. 문법 파헤치기 - *args의 위치
5. 문법 파헤치기 - **kwargs의 위치
6. 문법 파헤치기 - *args와 **kwargs 함께 사용하기

1. 사용 문법

먼저 다음과 같이 *args와 **kwargs 를 활용한 함수에 대해서 확인하겠습니다.

# *args and **kwargs 1
def foo(a, b, *args, **kwargs):
    print(a, b, args, kwargs)

foo(2, 9, 12, 34, x=3, name="bob")
2 9 (12, 34) {'x': 3, 'name': 'bob'}

*args를 사용하려면 *을 변수명 앞에 붙여서 함수를 선언합니다.
* 뒤에 함수명은 어떻게 해도 상관 없습니다.
영문명으로는 Variable Length Positional Arguments라고 합니다.

**kwargs를 사용하려면 변수명 앞에 **을 붙입니다.
마찬가지로 ** 뒤에 함수명은 어떻게 해도 상관 없습니다.
영문명으로는 Variable Length Keyword Arguments라고 합니다.

참고로 위 함수에서 a, b 같이 위치에 위해 사용되는 일반적인 arguments는
Regular Positional Arguments라고 합니다.

그리고 위 함수에 없지만 함수 선언 시, Default Value가 있는 것은 Default Arguments라고 합니다,

구체적 기능은 아래 이어서 하겠습니다.

2. args

args는 key word 되지 않은 형태의 정해지지 않은 수의 Arguments를 받습니다.
정해지지 않았으니 변수 개수와 상관없이 다 받아들일 수 있다는 것입니다.
위 함수 foo에서 args는 12와 34 입니다.

3. kwargs

kwargs는 key word가 있는 형태의 정해지지 않은 수의 Arguments를 받습니다.
args와 기능은 동일합니다.
위 함수 foo에서 kwars는 3, "bob"입니다.

4. 문법 파헤치기 - *args의 위치

다음과 같이 args를 사용한 함수가 있습니다.
그러나 아래와 같은 error가 발생합니다.
1개의 필요한 key-word가 있는 argument인 age를 놓쳤다고 하네요.

# *args and **kwargs 2
def func_param_with_var_args(name, *args, age):
    print("name=",end=""), print(name)
    print("args=",end=""), print(args)
    print("age=",end=""), print(age)


func_param_with_var_args("정우성", "01012341234", "seoul", 20)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: func_param_with_var_args() missing 1 required keyword-only argument: 'age'

왜 이런 오류가 발생했을까요?
*args 변수가 age로 입력할 20이라는 정수입력값까지 자신의 변수로 사용했기 때문입니다.
즉, 함수의 입력은 1개가 덜 들어오는 것으로 인식합니다.

내용을 수정하면 *args를 맨 뒤로 보내야 정상적으로 age를 입력 받을 것 같습니다.

# *args and **kwargs 3
def func_param_with_var_args(name, age, *args):
    print("name=",end=""), print(name)
    print("args=",end=""), print(args)
    print("age=",end=""), print(age)


func_param_with_var_args("정우성",20, "01012341234", "seoul")

name=정우성
args=('01012341234', 'seoul')
age=20

정상적인 결과가 나왔습니다!

5. 문법 파헤치기 - **kwargs의 위치

마찬가지로 kwargs를 사용한 함수가 있습니다.
그러나 아래와 같은 error가 발생합니다.
첫 줄 부터 에러가 발생합니다.
address=0에서 에러가 발생했습니다.

# *args and **kwargs 4
def func_param_with_kwargs(name, age, **kwargs, address=0):
    print("name=",end=""), print(name)
    print("age=",end=""), print(age)
    print("kwargs=",end=""), print(kwargs)
    print("address=",end=""), print(address)


func_param_with_kwargs("정우성", "20", mobile="01012341234", address="seoul")

위 함수가 문법적으로 허용 된다고 생각하면,
**kwargs가 들어가면 변수입력에서
**kwargs 이후로 key-word arguments 형태로 들어오는 변수는 kwargs 것이라고 생각할 것 입니다.
이렇게 하면, 조금 전 에러와 같이 입력 값 하나를 받지 않게 되는 형태가 됩니다.
그렇기 때문에 함수가 생성조차 되지 않았던 것이죠.

그렇다면, 3번의 # *args and **kwargs 2  코드에서 함수는 왜 생성이 되었는가? 하고 물어볼 수 있습니다.
사실 3번의 함수는 아래처럼 정상으로 출력될 수 있습니다.

# *args and **kwargs 5
def func_param_with_var_args(name, *args, age):
    print("name=",end=""), print(name)
    print("args=",end=""), print(args)
    print("age=",end=""), print(age)

>>> func_param_with_var_args("정우성", "01012341234", "seoul", age=20)
name=정우성
args=('01012341234', 'seoul')
age=20

*args는 **kwargs와 다르게 함수 선언시 변수 이후로 non-variable key-word arguments로 입력되는 값에 대해 정상적으로 받을 수 있습니다.

6. 문법 파헤치기 - *args와 **kwargs 함께 사용하기

유용한 기능이지만 순서를 유의할 필요가 있는 *args와 **kwargs를 살펴 보았습니다.
다음과 같이 *args와 **kwargs를 함께 사용했다고 생각해보겠습니다.

# *args and **kwargs 6
def mixed_params(name="아이유", *args, age, **kwargs, address):
    print("name=",end=""), print(name)
    print("args=",end=""), print(args)
    print("age=",end=""), print(age)
    print("kwargs=",end=""), print(kwargs)
    print("address=",end=""), print(address)


mixed_params(20, "정우성", "01012341234", "male" ,mobile="01012341234", address="seoul")

Traceback (most recent call last):
  File "python", line 18
    def mixed_params(name="아이유", *args, age, **kwargs, address):
                                                       ^
SyntaxError: invalid syntax

실행결과, 에러가 발생합니다.
이번에는 어떤 에러일까요?
순서와 같이 잘 못된 부분을 바로잡아 보겠습니다.

6-1) address 위치

kwargs 뒤에 변수가 오면 바로 에러가 발생하였습니다.
address라는 변수를 **kwargs 앞에 가져다 놓겠습니다.

# *args and **kwargs 7
def mixed_params(name="아이유", *args, age, address, **kwargs):
    print("name=",end=""), print(name)
    print("args=",end=""), print(args)
    print("age=",end=""), print(age)
    print("kwargs=",end=""), print(kwargs)
    print("address=",end=""), print(address)


mixed_params(20, "정우성", "01012341234", "male" ,mobile="01012341234", address="seoul")

Traceback (most recent call last):
  File "python", line 26, in <module>
TypeError: mixed_params() missing 1 required keyword-only argument: 'age'

또 다른 에러가 발생하였습니다.

6-2) non-default-value parameter가 default-value parameter 앞에

1)에서 non-default-value parameter인 즉, 함수 입력시 positional arguments로 받아지는 age는 default-value parameter인 name 보다 앞에 와야 합니다.

# *args and **kwargs 8
def mixed_params(age, name="아이유", *args, address, **kwargs):
    print("name=",end=""), print(name)
    print("args=",end=""), print(args)
    print("age=",end=""), print(age)
    print("kwargs=",end=""), print(kwargs)
    print("address=",end=""), print(address)

mixed_params(20, "정우성", "01012341234", "male" ,mobile="01012341234", address="seoul")

한 가지 이상한 부분이 있습니다.
함수에서 non-default-value parameter인 address는 default-value paramete인 name 앞에 오게하지 않았습니다.

address는 non-default-value parameter이지만, 동시에 Keyword-Only Arguments(키워드로만 받는 인자) 입니다.
조금 전 # *args and **kwargs 5 코드에서 *args 뒤에 age라는 Non-Default Keyword-Only Arguments가 올 수 있었습니다.
그리고 kwargs는 맨 뒤에만 올 수 있으므로 *args -> Non-Default Keyword-Only Arguments -> **kwargs의 순서로 함수의 arguments를 받아야 합니다.

그러면 결과적으로 다음과 같은 출력결과가 나옵니다.

name=정우성
args=('01012341234', 'male')
age=20
kwargs={'mobile': '01012341234'}
address=seoul

이를 최종적으로 정리한 도식화된 입력 내용이 있습니다.

<Variable/ Non-Varialbe & Default/NonDefault & Keyword only/Positional에 따른 function argument 순서>

조금 복잡하지만,
가장 일반적인 Positional Arguments는 가장 앞에
함수 입력시에 Default Value를 지정하는 Default Arguments를 그 다음
Variable Length Positional Arguments(*args)
함수 입력시에 Default Value를 설정하지 않았지만, Keyword를 입력받는 Non-Default Keyword-Only Arguments 또는
Default Value가 있지만 keyword도 받는 Keyword-Only Arguments with Defaults가 있습니다.(둘의 순서는 바뀌어도 상관 없음)
마지막으로 Variable Length Keyword Arguments(**kwargs)가 오면 됩니다.

위의 예제에서는 Keyword-Only Arguments with Defaults가 없어 추가 해보았습니다.

def mixed_params(age, name="아이유", *args, blood="O", address, **kwargs):
    print("name=",end=""), print(name)
    print("args=",end=""), print(args)
    print("age=",end=""), print(age)
    print("kwargs=",end=""), print(kwargs)
    print("address=",end=""), print(address)
    print("blood=", end=""), print(blood)


mixed_params(20, "정우성", "01012341234", "male" ,mobile="01012341234", address="seoul", blood="Rh+O")

blood라는 Default Value가 있으면서 keyword를 받는 변수는 **kwargs와 *args 사이에 있습니다.
말씀드린대로 blood와 address는 위치가 바껴도 정상 수행이 됩니다. 

reference: https://getkt.com/blog/python-keyword-only-arguments/

반응형