본문 바로가기

dev/python

[python] 함수 - *args, **kwargs

[python] 함수 - 함수 기본

[python] 함수 - 일반적이지 않은 함수


*args (가변매개변수)

 

만약에 매개변수가 몇개인지 모른다면 어떻게 할 것인가??

다음의 상황을 가정해보자

 

입력받은 값을 모두 더하는 함수를 만든다

하지만 어떤 숫자가 몇개나 입력될지는 모른다

 

이럴 때 사용하는게 가변인자

함수를 정의할 때 인수 자리에 *를 앞에 붙여서 아무 인수이름만 넣으면 된다

*args *a *abc *qwer 어떤 형태든 상관없다

다만 관례적으로 *args 를 많이 사용한다

def sum (*anything):
  result=0
  for i in anything:
    result+=i
  return result

print(sum(1,2,3,4,5))

>>15

 

*args 가 아니어도 정상적으로 함수는 동작하고 인수의 개수와 관계없이도 함수는 잘 돌아간다

 

그럼 다음의 예시도 보자

def calculate (a,*anything):
  if a=="plus":
    result=0
    for i in anything:
      result+=i
  elif a=="multiple":
    result=1
    for i in anything:
      result*=i
  return result

print(calculate('plus',1,2,3,4,5,6))
>>21

print(calculate('multiple',1,2,3,4,5,6))
>>720

위 예시는 변수 a에 오는 문자열에 따라 계산방식이 바뀐다

이처럼 가변인자만 사용하는 것이 아닌 가변인자와 일반적인 인자와도 함께 사용할 수 있다

 

다만 순서에는 유의해야 하는데 왜 그런지는 코드를 보며 알아보자

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


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 "/tmp/sessions/eb2e48f7310084ea/main.py",
line 14, in <module> func_param_with_var_args("정우성", "01012341234", "seoul", 20)
TypeError: func_param_with_var_args() missing 1 required keyword-only argument: 'age'

위의 함수는 정상적으로 동작하고 아래의 함수는 에러가 떴다

 

둘의 차이는 뭘까? 바로 인자의 위치다

가변인자는 가급적이면 일반 인자들 맨 뒤에 위치해야 한다

 

왜 그런지 에러코드를 보며 생각해보자

TypeError: func_param_with_var_args() missing 1 required keyword-only argument: 'age'

함수 func_param_with_var_args()에 필요한 1개의 'age'의 인자가 없다는 뜻이다

 

그 이유는 무엇일까?

 

위쪽과 아래쪽 함수가 매개변수를 받아들이는 순서를 보자

위쪽의 경우
name = '정 우 성'
age = 20
args = '01012341234', 'seoul'
아래쪽의 경우
name = '정 우 성'
args = '01012341234', 'seoul' 20
age = 

이런 순서로 매개변수를 입력받아오는데

두번째 함수에서는 가변인자가 전화번호부터 나이까지 전부 받아오기 때문에

age에 입력받을 함수가 없어져버린 것이다

엄밀히 말하면 가변인자가 가운데 있어서 그런 것이 아니라

가변인자 뒤의 매개변수가 입력받을 값도 가변인자가 가져갔기 때문이다

 

그럼 가변인자를 맨 뒤로 보내는 것 외에 별다른 해결책은 없을까?

만약 우리가 수정해야 할 인자의 자리가 1만줄이라면 하나하나 수정할 시간은 없을 것이다

 

다음의 경우로 해결할 수 있다

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

이렇게 age에 20을 직접 할당해주면 가변인자가 이미 할당된 20을 제외하고 나머지 인자들만 값을 받아오게 된다


**kwargs (키워드 가변인자)

 

이름에서 알 수 있듯, 키워드로 매개변수를 지정하되 매개변수가 몇개인지 모를 때 쓸 수 있따

가변인자처럼 인자의 개수가 달라진다거나 하는데에 영향을 받지 않고

가변매개변수처럼 순서를 신경쓰지 않아도 된다

코드를 읽는 사람이 어떤 파라미터가 어디로 전달되는지 좀 더 수월하게 알 수 있으니 가독성적인 측면에서도 좋다

매개변수 자리에 **kwarg를 입력하여 함수를 선언한다

 

 아래의 코드를 보자!

def print_kwarg(**kwarg):
	print(kwarg)


>>> print_kwargs(a=1)
{'a': 1}

>>> print_kwargs(product='candy', price=300)
{'product': 'candy', 'price':'300'}

인수자체를 바로 프린트 하면 Key-Value 가 한 쌍인 딕셔너리형태로 프린트되는걸 확인할 수 있다

 


Default Value & non-default Value

 

위 예시들에서도 나왔었는데 특정 밸류로 디폴트 값이 정해진 매개변수가 Default Value

그 외의 매개변수들을 non-Default Value 라고 한다

 

Default Value 들은 기본 값이 정의되어 있으므로 따로 정의하지 않아도 사용이 가능하다

def func(num1,num2=99):
    print(num1+num2)
    
func(2)
>>101

위 예시를 보면 num2를 함수 호출 시에 정의하지 않았음에도 결과값에는 반영된걸 볼 수 있다


이제 아래의 코드를 보자

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

>> SyntaxError: invalid syntax

 

매개변수를 정의할 때 항상 기억해야 한다

1. non-default value parameter를  default value parameter 앞에 선언하였는가 (어기면 구문 오류 남)

2. positional arguments(위치 인수)와 variable length arguments(가변 키워드 인수)의 위치를 확인하였는가

 

위 예시에서 name, address는 정의와 호출 시에 디폴트로 값이 정해졌고

kwarg와 함께  가변 키워드 인수라고 할 수 있다

 

age에 해당하는 값이 첫번째 자리에 호출되는걸 알았고 

기본값이 정의되지 않은 인자가 앞쪽으로 오게도 해야한다

 

위 조건들에 맞게 다시 재조정한 파라미터의 순서를 포함한 코드는 다음과 같다

def mixed_params(age, *args, name="아이유", 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")

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