2015년 4월 3일 금요일

파이선에서 *args와 **kwargs

*args는 함수에 임의 갯수의 argument를 넘겨주기 위해 사용된다. '임의의' 라는 단어가 여기서 매우 중요하고 기본적으로 파이선에서 *args의 역할을 이해하는 키가 된다. 
새로운것을 배우는 가장 좋은 방법은 실제로 사용해 보는 것이다. 간단한 예제로 multiply 함수를 만들길 원한다고 가정하자. Multiply 함수는 여러개의 argument를 인자로 받아 모두 곱한 결과를 화면에 출력한다. 하지만 이 함수가 몇개의 argument를 인자로 받는지 모른다. 그러므로 파이선 식으로 이 문제를 어떻게 해결할 수 있을까? *args에 대해 이미 알고 있다면 별로 어렵지 않다. multiply함수를 아래와 같이 정의하면서 *args를 인자로 넘겨주면 된다.

def multiply(*args):
  pass

하지만 multiply 함수를 만들기 전에 파이선 인터프리터 쉘을 열어 다음 코드를 실행해본다. 

def echo_args(*args):
  for arg in args:
    print arg

'*'는 args를 iterable로 만들어주어 for 루프를 실행해 args list 안에 있는 모든 argument를 처리할 수 있다는걸 의미한다. 예를 들어 echo_args 함수에 1,2,3을 argument로 넘겨주면 화면에 다음과 같이 출력되는걸 볼 수 있다.

>>> echo_args(1,2,3)
1
2
3
>>> 

이제 *args에 대해 명확하게 이해했을것이라 생각한다. 마지막으로 multiply 함수를 완성해 본다. operator에서 mul과 functools에서 reduce를 import한다.

from operator import mul
from functools import reduce

def multiply(*args):
  return reduce(mul, args)

* mul과 reduce는 *args와는 아무 관계가 없다. 단지 함수를 만드는걸 도와주는 툴일 뿐이다.

정리하자면 *args는 사용자에 의해 함수에 몇개의 argument가 넘겨질 지 모를때 사용된다. 

def get_arguments(a, *args):
  print "The first argument is ", a
  for arg in args:
    print "This argument come from args: ", arg

다음은 위의 함수 get_argements의 사용 예이다.

>>> get_arguments(13,13,13)
The first argument is 13
This argument come from args: 13
This argument come from args: 13
>>>

이제 elements라는 변수에 몇개의 element를 가지는 tuple을 만들어준다.

>>> elements = (1, 2, 3)
>>>

그리고 elements를 get_arguments에 넣어 호출해 본다.

>>> get_arguments(elements)
The first argument is (1, 2, 3)
>>>

위의 출력 결과에서 볼 수 있는것처럼 elements를 함수에 넘겨주면 elements가 a에 할당된다.
그럼 다음과 같이 하면 어떻게 되는가 보자.

>>> get_arguments(*elements)
The first argument is 1
This argument come from args: 2
This argument come from args: 3

'*'는 튜플 elements를 unpack하기 때문에 get_arguments(1,2,3)을 호출한것과 같은 의미가 된다. 

----------------------

**kwargs는 *args와 마찬가지로 함수 선언에 사용되는 마법 변수지만 차이점은 함수에 keyworded argement를 넘겨줄 수 있는 것이다.

예제를 보도록 하자.

def display_stuff(**kwargs):
  if kwargs is not None:
    print kwargs

이제 다음과 같이 함수를 호출해 본다.

>>> display_stuff(name='Jeff', passion='development', language='python')
{'passion': 'development', 'name': 'Jeff', 'language': 'python'}
>>>

결과에서 볼 수 있는것처럼 **kwargs는 함수에 임의의 keyworded arguemts를 넘겨줘 함수 내에서 dictionary로 억세스 할 수 있게 해 준다. 예를들어 다음의 코드를 사용하면 키 'passion'에 대한 값을 쉽게 출력할 수 있다.

def display_stuff(**kwargs):
  if kwargs is not None:
    print kwargs['passion']

이제 다시 함수를 호출해 본다.

>>> display_stuff(name='Jeff', passion='development', language='python')
development
>>>

하지만 kwargs dictionary에 'passion' 키가 없는 경우는 어떻게 될까? KeyError가 발생하게 된다. 

>>> display_stuff(error='KeyError')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in display_stuff
KeyError: 'passion'
>>>

에러를 피하고 싶으면 kwargs를 확인할 때 억세스하려는 키가 dictionary에 존재하는지 체크할 수 있다.

def display_stuff(**kwargs):
  if kwargs is not None and 'passion' in kwargs:
    print kwargs['passion']

함수를 위와 같이 바꾸면 'passion' 키가 없어도 더 이상 KeyError가 발생하지 않는다. 

dictionary 내의 모든 키와 값을 출력하고 싶으면 다음과 같이 하면 된다.

def display_stuff(**kwargs):
  if kwargs is not None:
    for key, value in kwargs.items():
      print key, '==>', value

이제 실행해 본다.

>>> display_stuff(name='Jeff', passion='development')
passion ==> developer
name ==> Jeff
>>>

지금까지 본 것 처럼 *args와 **kwargs는 둘의 차이점을 이해하고 잘 사용하면 파이선 프로그래밍에 매우 유용하다.

댓글 1개:

  1. 감사합니다!!! 출처 남기고 블로그에 참조하겟습니다!
    http://blog.naver.com/bellorossopomodor

    답글삭제