[PythonBasic] 가변 인수(*args)와 키워드 가변 인수(**kwargs)

Alex of the year 2020 & 2021·2020년 6월 25일
1

Python

목록 보기
6/18
post-thumbnail

*args & ** kwargs

1) *args

영어로 하면 variable length arguments
conventional하게 *args라고 쓸뿐이지, *alex, *apple, *ant 뭐라고 쓰든 자유다
복수의 일반 인자를 사용자에게 입력받아서 사용할 때 쓰인다
(정확히는, 정해지지 않은 수의 일반 인자)
--> 여러개의 인자를 받은 경우, 함수 내부에서는 튜플로 받은 것처럼 인식한다.

*args를 사용할 때는 주의할 점이 몇 가지 있다
다음의 코드를 보자

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 "/Users/soomialexhwang/Desktop/rfsd.py", line 7, in <module>
    func_param_with_var_args("정우성", "01012341234", "seoul", 20)
TypeError: func_param_with_var_args() missing 1 required keyword-only argument: 'age'

이게 왜 에러가 났냐하면, 함수 선언부에 그 이유가 있다.
*args는 사용자가 몇 개의 인수를 입력할지 모르는 상황, 즉 여러 개의 인수를 입력받을 수도 있는 경우에 사용한다고 하였다
요 바로 직전 포스팅에서 적었듯 이 함수는 첫 선언 시 파라미터 정의에 민감하다
내 생각에 기본 형식을 벗어나는 파라미터 정의는 무조건 마지막에 나오는 것이 맞다

에러를 맞지 않으려면 다음과 같이 정의하는 것이 좋다

def func_param_with_var_args(name, age, *args):
    print("name=",end=""), print(name)
    print("age=",end=""), print(age)
    print("args=",end=""), print(args)

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

age와 args가 순서가 바뀌어있다.
그리고 이렇게 실행하면?

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

로 잘 실행되는 것을 볼 수 있다.


2)**kwargs

영어로 하면 keyword arguments
역시, conventional하게 *kwargs라고 쓸뿐이지, *kite, *kitten, *king 뭐라고 쓰든 자유다
*args와 다른 점이라면, **kwargs는 키워드로 이루어진 복수의 인자를 받는다
(정확히는, 정해지지 않은 수의 키워드로 이루어진 인자)

자 다음의 코드를 보고 왜 안되는 지 생각해보자

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")

이 코드는 실행이 아예 되지 않는다 (Invalid Syntax)
어떻게 해야 실행이 될 수 있을까 고민해보았다.

**kwargs 같은 특수 인자 역시, 위에서 *args 에서처럼 맨 뒤에 위치해야 하는 것 아닐까?

그래서 코드에서 address와 자리를 바꾸어서 다시 실행했다

>>>
name=정우성
age=20
kwargs={'mobile': '01012341234'}
address=seoul

잘 출력되었다!
(참고로 함수 정의 시, address=0이라는 파라미터가 영 이상해서, address만으로도 실행해보았는데 결과는 똑같이 출력되었다.)

3) *args**kwargs를 동시에

바로 다음의 코드를 보자

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")

솔직히 조금 예상했는데, 실제로 IDLE에서 실행 자체가 되지 않았다 (Invalid Syntax)

예상한 문제점
1. *args 뒤에 일반 인자인 age가 있다
2. **kwargs 뒤에 일반 인자인 address가 있다
3. 게다가 name="아이유"는 Default 인자! --> 맨 앞에서 정의되어서는 안된다고 했다
4. 근데 여기서 잠깐!
*args**kwargs, default인자의 서열은 어떻게 처리하지?

first try

사실 그냥 느낌대로 IDLE을 한 번 돌려보고 싶어서 우선은
age, address, *args**kwargs, default인자 순서로
파라미터를 지정한 후, 코드를 실행시켰다.

실행 결과는 역시 Invalid Syntax 에러!


second try

그럼 이번엔 조금 바꾸어서
age, address, default인자,*args**kwargs 순서로
실행

Traceback (most recent call last):
  File "/Users/soomialexhwang/Desktop/rfsd.py", line 19, in <module>
    mixed_params(20, "정우성", "01012341234", "male" ,mobile="01012341234", address="seoul")
TypeError: mixed_params() got multiple values for argument 'address'

실행은 되는데 에러가 나온다
근데 에러 내용을 조금 읽어보니,
앗차 알겠구나 address가 이미 default값으로 지정되었는데 파라미터로 또 지정한 것이다!

함수 지정시의 파라미터 순서만 너무 신경쓰느라,
막상 함수에 투입되는 파라미터를 보지 않았다
함수에 투입되는 파라미터를 다시 바로 잡을 필요가 있어 보여서 코드를 수정했다


third try

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


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

이번엔 될 줄 알았다 솔직히
근데

내가 못보고 넘어가는게 있나? 싶어서 다시 한 번 원래 코드로 돌아가서 찬찬히 보며 분석했다

다시 한 번 이 코드의 문제점을 찾자

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")
  1. 가변인자 *args가 일반인자age, address 보다 뒤에 위치해있다
  2. 키워드 가변인자 **kwargs도 다소 앞에 위치해있다.
  3. name="아이유"는 디폴트값 인자인데 맨 앞에 있다. 디폴트값 인자는 맨 뒤에 위치한다.
  4. 근데, 여기서 하나 더 보이는게 생겼다

address에 넣는 인자 값을 보면,
address="seoul"이라고 쓰고 있는데 이런 인자는 앞에서 본적이 없는데?

찾아보니 Keyword-Only arguments라고 부르는 특수 인자값으로
이는 *args보다는 뒤에 위치한다.

아 그럼 다시 짜보자;;

fourth try

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


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

자 마지막이기를 바라며 내가 짠 코드 리뷰해보기

함수 정의 시 순서:
일반 인자 -> default 일반 인자 -> 가변 인자 -> Keyword-Only 인자 -> 키워드 가변 인자

실행 결과는?

나왔다
😭
네시간 싸웠다 ;;;;;



🍎 그렇다면 절대적인 순서와 규칙을 다음의 사진을 보며 다시 한 번 정리

def 함수명( ⭐️ ):
⭐️ 위치에 인자를 정의하는 순서와 규칙

  1. 일반 positional 인자
  2. 디폴트 인자 (이미 값을 지정한 인자)
  3. 가변 인자 (*args)
  4. 디폴트가 아닌 Keyword-Only 인자
  5. 디폴트 Keyword-Only 인자
  • 단 4, 5번은 (4, 4', 5, 5')식의 정렬이 아니라 (4, 5, 4', 5') 식으로 정렬
  1. 가변 키워드 인자 (**kwargs)


📖 references
개미님의 브런치 글: https://brunch.co.kr/@princox/180
args와 kwargs 정리해주신 또 다른 분 블로그: https://m.blog.naver.com/PostView.nhn?blogId=wideeyed&logNo=221455090671&proxyReferer=https:%2F%2Fwww.google.com%2F
(당연히) 점프 투 파이썬: https://wikidocs.net/book/1
https://getkt.com/blog/python-keyword-only-arguments/

profile
Backend 개발 학습 아카이빙 블로그입니다. (현재는 작성하지 않습니다.)

1개의 댓글

comment-user-thumbnail
2020년 11월 22일

정리 너무 잘하신거 같아요 감사합니다 !!

답글 달기