astericks, args, kwargs in python

3 분 소요

args, kwargs 를 정리해봅시다.

  • 사실 decorator를 쓸 때가 아니면, 아직까지는 반드시 args, kwargs가 반드시 필요하다고 느낀 적이 없기는 한데, 다른 라이브러리들을 보다보면, args, kwargs 를 사용하는 일들이 좀 있어요.
  • 그래서 이참에 좀 다시 정리해보기로 했습니다.

  • 간단하게 a, b를 argument로 받아서 합을 계산해주는 함수를 만들었습니다.
    • 두번째 함수 콜에서는 에러가 발생합니다. 사실 당연하죠. argument의 수가 더 많으니까요.
def func1(a, b):
    return a+b

print(func1(1, 2)) ## 실행됨 
print(func1(1, 2, 3)) ## argument의 수가 많으므로 실행이 안됨 
3
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-625-1a02d7b5e7f9> in <module>()
      3 
      4 print(func1(1, 2))
----> 5 print(func1(1, 2,3))

TypeError: func1() takes 2 positional arguments but 3 were given

args: variable-length argument list

  • variable-length argument, 한국말로 하면 “가변적 인자 수”라고 표현할 수 있을까요?
  • 함수 인자를 미리 정해두지 않고, 들어오는 수에 맞춰서 함수를 돌릴 수 있도록 해봅니다.
    • 물론 그냥 list를 넘기면 되는거 아니냐? 라고 묻는다면 네…사실 그렇죠.
  • 하지만, 자주 쓰는 print함수를 생각해봅시다. 아래처럼 여러 개를 한번에 넘겨도 알아서 잘 실행됩니다. print 함수가 이렇게 argument의 수가 가변적이라는 것을 알 수 있습니다.
print("dd")
print("dd", 1, 2)
dd
dd 1 2
  • 아래처럼 *args로 함수인자를 정의하면, 몇 개가 들어오든 상관없습니다.
    • 그리고 사용할 때는 args로 사용하면 되는데 얘는 tuple이에요.
    • 또한 혹시나 해서 말하자면, 들어오는 변수의 타입이 같을 필요도 없습니다.
## 함수 선언부에는 *args 로 써주고 
## 값을 사용할 때는 args 로 사용합니다. 
def func1(*args):
    print(type(args))
    print(sum(args))
    print("="*20)

func1(1, 2)
func1(1, 2, 3)
func1(1, 2, 3, 4)
<class 'tuple'>
3
====================
<class 'tuple'>
6
====================
<class 'tuple'>
10
====================

kwargs: keyworded, variable-length argument dictionary

  • args의 경우는 tuple로 제공됩니다. 즉 integer indexing을 해야 한다는 이야기인데요.
  • integer indexing보다 keyword로 indexing하고 싶을 경우에는 kwargs를 사용하면 됩니다.
  • 단 이 아이는 별이 두 개! 붙어야 합니다.

별이 다섯개!!

  • 아래 보시는 것처럼 알아서 잘 해석해서 넘겨줍니다 하하핫
def func2(**kwargs):
    print(type(kwargs))
    for k in kwargs.keys():
        print("{}: {}".format(k, kwargs[k]))
    print("="*20)

func2(name='Lee', age='25')
func2(name='Kim', age='25', gpa=4.3)
<class 'dict'>
name: Lee
age: 25
====================
<class 'dict'>
name: Kim
age: 25
gpa: 4.3
====================

hybrid

  • 둘 중에 하나만 써야 하는 것이 아니고, 다음처럼 함께 쓸 수 있습니다.
  • 단.
    • func_hybrid(**kwargs, *args)와 같은 식으로 순서를 바꿔서 함수를 정의하면 안되고
    • func_hybrid(1,2,3, name='Lee', 4)처럼 args, kwargs 가 섞여 있는 형태로 함수를 콜하면 안됩니다.
## hybrid
def func_hybrid(*args, **kwargs):
    print("args")
    for i, v in enumerate(args):
        print("{}: {}".format(i, v))
    print("-"*20)
    for k, v in kwargs.items():
        print("{}: {}".format(k, v))
    print("="*20)

func_hybrid(1,2,3, lambda x:x+10, name='Lee', age=25, gpa=4.3, f = lambda x: x+1)
args
0: 1
1: 2
2: 3
3: <function <lambda> at 0x11c3ba598>
--------------------
name: Lee
age: 25
gpa: 4.3
f: <function <lambda> at 0x11c3ba7b8>
====================

naming

  • args, kwargs로 써야 하는 것은 아닙니다.
  • 이름은 달라도, 별이 하나 인지 두개 인지에 따라서 알아서 args, kwargs로 인식하게 됩니다.
## 이름이 똑같지 않아도 됩니다. 
def func_different_name(*input_tuple, **input_dict):
    for i, v in enumerate(input_tuple):
        print("{:2d}: {}".format(i, v))
    print("=== args over ===")
    for k, v in input_dict.items():
        print("{}: {}".format(k, v))
    print("=== kwargs over ===")
func_different_name(1,2,3, name='lee', age=35)
 0: 1
 1: 2
 2: 3
=== args over ===
name: lee
age: 35
=== kwargs over ===

별이 붙은 것, 붙지 않은 것

  • 별이 붙은 것을 출력하는 경우와, 별이 붙지 않은 것을 출력하는 경우가 다릅니다.
  • 아래를 보시면
    • 별 없이 출력하는 경우: 튜플을 출력
    • 별과 함께 출력하는 경우: 연속된 argument를 그대로 출력
def func4(*args):
    print(args)## tuple로 변화된 값을 출력 
    print(*args)## 연속된 값을 그대로 출력 함 
func4(1,2,3)
(1, 2, 3)
1 2 3
  • print가 아니라 이를 함수에 넘긴다고 생각하면,
    • 별이 없는 경우는 단 하나의 argument가 tuple로 넘어가는 것이고
    • 별이 있는 경우는 여러 개의 argument가 넘어가는 형태입니다.
def func4(*args):
    def print_func(*args):
        for i, v in enumerate(args):
            print("{}: {}".format(i, v))
        print("#"*20)
    ## 아래의 경우 단 하나의 argument가 넘어가는 것이고 
    print_func(args)
    ## 아래의 경우는 여러 개의 argument가 넘어오는 것 
    print_func(*args)
func4(1,2,3)
0: (1, 2, 3)
####################
0: 1
1: 2
2: 3
####################

wrap-up

  • decorator를 쓸 때는 이 구분이 유의미할 것 같은데, 음….반드시 알아야 하는것인가? 잘 모르겠습니다 허허헛

reference

댓글남기기