함수의 파라미터 args & kwargs
파이썬 코드를 구경하다보면 함수의 입력변수로 다음과 같은 것들을 참 많이 보게 된다.
*args
**kwargs
예를 들면 다음과 같은 함수가 있을 수 있다. (여기서 사용된 함수명인 func는 임의의 함수명이다.)
def func(*args):
# func body
또는 다음과 같은 함수
def func(**kwargs):
# func body
심지어 두개가 함께 쓰인 함수도 보인다.
def func(*args, **kwargs):
# func body
도대체 이러한 함수들에서 사용되는 *args
와 **kwargs
는 어떤 의미를 가지고 있길래 이렇게 자주 사용되는 것일까?
하나씩 알아보도록 하자.
*args
우선 *args
이다.
*args
에 사용된 args라는 이름은 관례적인 표기명(convention)이다. 꼭 "args"라는 이름을 사용할 필요는 없지만 이왕이면 이런 이름을 사용하기를 바란다는 뜻이다. 이것은 args 뿐만 아니라 kwargs에도 해당된다. 잠시 그 약어의 의미에 대해서 풀어써 보면 다음과 같은 의미가 될 것이다.
- args - arguments의 약어
- kwargs - keyword arguments의 약어
이제 *args
의 사용법에 대해서 다음 예제를 통해 알아보자.
>>> def func(*args):
... print(args)
...
우선 위와 같은 간단한 함수를 먼저 작성해 보자. 그리고 이 함수를 다음과 같이 사용해 보자.
>>> func(1, 2, 3)
(1, 2, 3)
>>> func(1, 2)
(1, 2)
>>> func('a', 'b', 'c', 'd')
('a', 'b', 'c', 'd')
입력이 1, 2, 3인 경우 args에는 (1, 2, 3)이라는 튜플이 대입되었고 입력이 1, 2인 경우에는 args변수에 (1, 2)라는 튜플이 대입된다. 이 사실로 유추해보면 입력인수로 *args
를 사용하면 입력 인수의 갯수에 상관없이 입력되는 모든 항목이 args라는 튜플로 저장된다는 것을 알 수 있다.
즉, *args
는 모든 입력인수를 튜플로 변환해 주는 변수라고 할 수 있겠다.
**kwargs
이번에는 **kwargs
에 대해서 알아보자. **kwargs
는 *args
와는 달리 별표시(*
)가 두 개 사용된다. 역시 이것도 예제로 알아보도록 하자.
>>> def func(**kwargs):
... print(kwargs)
...
그리고 이 함수를 다음과 같이 사용해 보자.
>>> func(a=1)
{'a': 1}
>>> func(name='foo', age=3)
{'age': 3, 'name': 'foo'}
func 함수의 인수로 key=value 형태를 주었을 때 입력 값 전체가 kwargs라는 딕셔너리에 저장된다는 것을 알 수 있다.
즉, **kwargs
는 모든 key=value 형태의 입력인수를 딕셔너리로 변환해 주는 변수라고 할 수 있겠다.
이번에는 다음과 같은 형태의 호출에 대해서 생각해 보자.
>>> func(1, 2, 3, name='foo', age=3)
위 예처럼 이렇게 입력인수의 형태가 다양한 경우 입력인수의 갯수에 상관없이 입력을 받고 싶다면 어떻게 해야 할까?
이런경우 다음의 함수를 사용하면 입력인수를 모두 처리할 수 있게 된다.
>>> def func(*args, **kwargs):
... print(args)
... print(kwargs)
...
>>> func(1, 2, 3, name='foo', age=3)
(1, 2, 3)
{'age': 3, 'name': 'foo'}
1, 2, 3 과 같은 일반적인 입력은 args튜플에 저장되고 name='foo' 와 같은 형태의 입력은 kwargs 딕셔너리에 저장되는 것을 확인할 수 있다.
만약 다음과 같이 호출한다면 어떻게 될까?
>>> func(1, 2, 3, name='foo', age=3, 4, 5)
일반적인 입력인수가 키워드형태의 입력 뒤에 존재하는 경우이다. 호출하면 다음과 같은 오류를 만나게 된다.
>>> func(1, 2, 3, name='foo', age=3, 4, 5)
File "<stdin>", line 1
SyntaxError: non-keyword arg after keyword arg
키워드 형태의 인수뒤에 키워드 형태가 아닌 인수는 올 수 없음을 알 수 있다. 따라서 다음과 같은 형태의 호출로 변경해야 할 것이다.
>>> func(1, 2, 3, 4, 5, name='foo', age=3)
(1, 2, 3, 4, 5)
{'age': 3, 'name': 'foo'}
클래스 입력 인수
이렇게 튜플이나 딕셔너리 형태의 입력인수를 취하는 함수가 가장 많이 사용되는 경우는 클래스를 상속하여 기능을 추가할 경우이다.
다음의 예제를 보자.
class Parent:
def __init__(self, value1, value2):
print(value1, value2)
class Child(Parent):
def __init__(self, *args, **kwargs):
Parent.__init__(self, *args, **kwargs)
# 뭔가 다른일을 수행한다.
print("hello")
if __name__ == '__main__':
child = Child(1,2)
Child클래스는 Parent클래스를 상속하였다. 그리고 Child 클래스는 생성자(__init__
)에서 Parent의 생성자를 그대로 수행한 후에 또 다른 기능을 추가 하였다.
이때 Child 클래스의 생성자는 Parent 클래스의 생성자를 만드는 규칙에 상관없이 항상 동작시키게 만들기 위해서 *args, **kwargs
입력인수를 사용했다. 입력으로 받은 *args, **kwargs
값을 그대로 Parent클래스의 생성자에 전달하면 입력 인수의 갯수와 종류에 상관없이 항상 동작하기 때문이다. 이럴 경우 Parent클래스의 생성자가 변경되더라도 Child클래스는 수정할 필요가 없게 되는 장점이 있다.
위 예제를 실행하면 다음과 같은 결과가 출력될 것이다.
1 2
hello
박응용 님
1405
2020년 7월 8일 9:15 오후