파이썬 | 위치 인수와 키워드 인수

CHOI·2021년 11월 24일
0

Python

목록 보기
23/33
post-thumbnail

지금까지 간단하게 문자열을 출력하는 함수와 두 수를 더하는 함수를 만들었다. 파이썬에서는 함수를 좀 더 편리하게 사용할 수 있도록 다양한 기능을 제공한다. 이번에는 함수에서 위치 인수키워드 인수를 사용하는 방법과 리스트와 딕셔너리의 언패킹을 활용하는 방법에 대해서 알아보자.

1. 위치 인수와 리스트 언패킹

>>> print(10, 20, 30)
10 20 30

위와 같이 함수에 인수를 순서대로 넣는 방식을 위치 인수(positional argument) 라고 한다. 즉, 인수의 위치가 정해져있다. print 에 10, 20, 30 을 순서대로 넣어서 출력할 때도 10, 20, 30 순서대로 출력이 된다. 우리가 지금까지 흔히 사용한 방법이다.

위치 인수를 이용하는 함수

그렇다면 간단하게 숫자 3개를 출력하는 함수를 만들어보자.

def print_number(a, b, c):
		print(a)
		print(b)
		print(c)

print_number(10, 20, 30)

출력 결과

10
20
30

언패킹 사용하기

이와 같이 인수를 순서대로 넣을 때 리스트나 튜플을 사용할 수도 있다. 다음과 같이 리스트나 튜플 앞에 * (에스터리스트)를 붙여서 함수에 넣어주면 된다.

  • 함수(*리스트)
  • 함수(*튜플)
>>> x = [10, 20, 30]
>>> print_numbers(*x)
10
20
30

리스트 x* 만 앞에 붙여줬을 뿐인데 함수의 인수로 10, 20, 30이 순서대로 들어간다. 즉, 리스트나 튜플 앞에 * 를 붙여주면 언패킹(unpacking)이 되어 print_numbers(10, 20, 30) 과 똑같은 동작이 된다. 말 그대로 리스트의 포장을 푼다는 의미이다.

>>> print_numbers(*[10, 20, 30])
10
20
30

리스트 변수 대신 리스트 앞에 바로 * 를 붙여도 동작은 같다.

단, 이때 주의할 점은 인수의 개수와 리스트 요소의 개수가 일치해야 한다는 점이다. 만약 개수가 다르다면 함수를 호출할 수 없다. 여기에서는 print_numbers 함수가 인수 3개를 받으니까 리스트나 튜플의 요소의 개수가 3개인 것을 건내줘야 한다. 만약 그렇지 않다면 오류가 발생한다.

>>> print_numbers(*[10, 20])
Traceback (most recent call last):
  File "<pyshell#16>", line 1, in <module>
    print_numbers(*[10, 20])
TypeError: print_numbers() missing 1 required positional argument: 'c'

가변 인수 함수 만들기

그러면 위치 인수와 리스트 언패킹은 어디에서 사용할까? 이 기능들은 인수의 개수가 정해져 있지 않은 가변 인수(variable argument)에 사용한다. 즉, 같은 함수에 인수 한 개를 넣을 수 있고, 열 개를 넣을 수 있다. 또는 인수를 안 넣을수도 있다.

다음과 같이 가변 인수 함수는 매개변수 앞에 * 를 붙여서 사용한다.

def 함수이름(*매개변수):
    코드

그러면 앞서 만든 숫자를 출력하는 함수를 가변 인수 함수로 만들어보자.

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

위에 *args 와 같이 매개변수 앞에 * 를 붙여주면 된다. 그리고 forargs 를 반복해서 출력해주면 된다.

매개변수 이름은 마음대로 지어도 되지만 관례적으로 arguments 를 줄여서 args 로 사용한다. 특히 이 args 는 튜플이여서 for 로 반복할 수 있다.

>>> print_numbers(10)
10
>>> print_numbers(10, 20, 30, 40)
10
20
30
40

그러면 print_numbers 함수에 숫자를 한 개 넣으면 한 개가 출력되고 네 개 넣으면 네 개가 출력된다. 즉, 넣은 숫자 만큼 출력이 되는 것이다. 위와 같이 함수에 인수를 여러개 넣어도 되고

>>> x = [10]
>>> print_numbers(*x)
10
>>> y = [10, 20, 30, 40]
>>> print_numbers(*y)
10
20
30
40

위와 같이 숫자가 들어있는 리스트(튜플)을 넣고 언패킹을 해줘도 된다.

% 고정 인수와 가변 인수 동시에 사용하기

>>> def print_numbers(a, *args):
...     print(a)
...     print(args)
...
>>> print_numbers(1)
1
()
>>> print_numbers(1, 10, 20)
1
(10, 20)
>>> print_numbers(*[10, 20, 30])
10
(20, 30)

고정 인수와 가변 인수를 동시에 사용할 때는 위와 같이 고정 인수를 먼저 지정하고 그 다음에 가변 인수를 지정해주면 된다.

단, 이때 def print_numbers(*args, a): 처럼 고정 매개변수 보다 *args 가 먼저 오면 안된다. 매개변수 순서에서 *args 는 항상 맨 마지막이여야 한다.

2. 키워드 인수 사용하기

지금까지 함수에 인수를 넣을 때 값이나 변수 그대로 넣었다. 그러다 보니 각각의 인수가 어떠한 용도인지 알기 어려웠다. 보통 함수에 사용 방법을 익힐 때 인수의 순서와 용도를 함께 익힌다.다음 코드를 보자. 개인 정보를 출력하는 함수이다.

>>> def personal_info(name, age, address):
...     print('이름: ', name)
...     print('나이: ', age)
...     print('주소: ', address)
...

이 함수는 첫 번째 인수는 이름, 두 번째 인수는 나이, 세 번째 인수는 주소이다. 만약 인수의 순서가 바뀌면 나이에 이름이 들어가고 주소에 이름이 들어가는 등 잘못된 결과가 나올 것이다.

>>> personal_info('홍길동', 30, '서울시 용산구 이촌동')
이름:  홍길동
나이:  30
주소:  서울시 용산구 이촌동

이와 같이 함수를 사용할때 인수의 순서와 용도 모두 기억해야 한다는 불편한점이 있다. 그래서 파이썬에서는 순서와 용도를 매번 기억하지 않도록 키워드 인수(keyword argument)라는 기능을 제공한다. 키워드 인수는 말 그대로 인수에 이름(키워드)를 붙이는 것이다.

  • 함수(키워드=값)

personal_info 함수를 키워드 인수 방식으로 호출해보자.

>>> personal_info(name='홍길동', age=30, address='서울시 용산구 이촌동')
이름:  홍길동
나이:  30
주소:  서울시 용산구 이촌동
>>> personal_info(age=30, address='서울시 용산구 이촌동', name='홍길동')
이름:  홍길동
나이:  30
주소:  서울시 용산구 이촌동

키워드 인수를 사용하니까 인수의 용도가 정확히 보이고 특히 인수의 순서를 지키지 않아도 키워드에 해당값이 잘 들어간다. 이전에 personal_info 함수에는 이름, 나이, 주소 순서대로 인수를 넣어야 했지만 키워드 인수를 사용하면 순서를 지키지 않아도 되는 것이다.

print(10, 20, 30, sep=':', end='')

참고로 우리가 이전에 print 에서 사용했던 sep 이나 end 도 키워드 인수이다.

3. 키워드 인수와 딕셔너리 언패킹

지금까지 함수를 호출할 때 키워드 인수로 직접 값을 넣었다. 이번에는 딕셔너리를 사용하여 키워드 인수로 값을 넣는 딕셔너리 언패킹에 대해서 알아보자

  • 함수(딕셔너리)**

다음과 같이 딕셔너리 앞에 ** (에스터리스크 두 개)를 붙여서 함수에 넣어준다. 먼저 personal_info 함수를 만들어보자.

>>> def personal_info(name, age, address):
...     print('이름: ', name)
...     print('나이: ', age)
...     print('주소: ', address)
...

이제 '키워드' : 값 형식의 인수를 저장하고 앞에 ** 를 붙여서 함수에 넣어준다. 이때 딕셔너리 키워드(키)는 반드시 문자열 형태여야 한다.

>>> x = {'name': '홍길동', 'age': 30, 'address': '서울시 용산구 이촌동'}
>>> personal_info(**x)
이름:  홍길동
나이:  30
주소:  서울시 용산구 이촌동

딕셔너리에 저장된 값들이 잘 출력되었다. **x 처럼 딕셔너리를 언패킹을 하면 딕셔너리의 값들이 함수에 잘 들어가서 personal_info(name='홍길동', age=30, address='서울시 용산구 이촌동') 또는 personal_info('홍길동', 30, '서울시 용산구 이촌동') 과 똑같은 동작이 된다.

>>> personal_info(**{'name': '홍길동', 'age': 30, 'address': '서울시 용산구 이촌동'})
이름:  홍길동
나이:  30
주소:  서울시 용산구 이촌동

또한 딕셔너리 변수 대신 딕셔너리 앞에 바로 ** 를 붙여도 된다.

딕셔너리 언패킹을 사용할 때는 함수의 매개변수 이름과 딕셔너리의 키 이름이 같아야 한다. 또한 딕셔너리 키의 개수와 매개변수 개수가 같아야 한다. 그렇지 않으면 오류가 발생한다.

>>> personal_info(**{'name': '홍길동', 'old': 30, 'address':'서울시 용산구 이촌동'})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: personal_info() got an unexpected keyword argument 'old'
>>> personal_info(**{'name': '홍길동', 'age': 30})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: personal_info() missing 1 required positional argument: 'address'

**(에스터리스크) 를 두 번 사용하는 이유

그렇다면 왜 리스트 언패킹 처럼 * 를 한 번 사용하지 않고 두 번 사용할까? 그 이유는 딕셔너리는 키-값 형태로 값이 저장되어 있기 때문이다. 먼저 * 를 한 번만 사용해서 함수를 호출해보자.

>>> x = {'name': '홍길동', 'age': 30, 'address': '서울시 용산구 이촌동'}
>>> personal_info(*x)
이름:  name
나이:  age
주소:  address

위와 같이 에스터리스크를 한 번만 사용하면 키가 출력된다. 즉, 딕셔너리를 한 번 언패킹 하면 키를 사용한다는 의미이다. 따라서 ** 처럼 언패킹을 두 번 사용하여 키의 값을 사용하도록 만들어야 한다.

>>> x = {'name': '홍길동', 'age': 30, 'address': '서울시 용산구 이촌동'}
>>> personal_info(**x)
이름:  홍길동
나이:  30
주소:  서울시 용산구 이촌동

키워드 인수를 사용하는 가변 인수 함수 만들기

이번에는 키워드 인수를 사용하면 가변 인수 함수를 만들어보자

def 함수이름(**매개변수):
    코드

위와 같이 키워드 인수를 사용하는 가변 인수 함수는 매개변수 앞에 ** 를 붙여서 만든다

>>> def personal_info(**kwargs):
...     for kw, arg in kwargs.items():
...         print(kw, ': ', arg, sep='')
...

매개변수 이름은 마음대로 지어도 되지만 관례적으로 keyword arguments 를 줄여서 kwargs 를 사용한다. 또한 kwargs 는 딕셔너리여서 for 반복문을 활용할 수 있다.

>>> personal_info(name='홍길동')
name: 홍길동
>>> personal_info(name='홍길동', age=30, address='서울시 용산구 이촌동')
name: 홍길동
age: 30
address: 서울시 용산구 이촌동
>>> x = {'name': '홍길동'}
>>> personal_info(**x)
name: 홍길동
>>> y = {'name': '홍길동', 'age': 30, 'address': '서울시 용산구 이촌동'}
>>> personal_info(**y)
name: 홍길동
age: 30
address: 서울시 용산구 이촌동

딕셔너리 x 는 {'name': '홍길동'} 이므로 personal_info(**x) 로 호출하면 personal_info(name='홍길동') 과 같고 딕셔너리 y 는 {'name': '홍길동', 'age': 30, 'address': '서울시 용산구 이촌동'} 이므로 personal_info(**x) 로 호출하면 personal_info(name='홍길동', age=30, address='서울시 용산구 이촌동') 과 같다.

이처럼 함수를 만들 때 def personal_info(**kwargs): 와 같이 매개변수에 ** 를 붙여주면 키워드 인수를 사용하는 가변 인수 함수를 만들 수 있다. 그리고 이러한 함수를 호출할 때 키워드와 인수를 각각 넣거나 딕셔너리 언패킹을 사용하면 된다.

보통 **kwargs 를 사용하는 가변 인수 함수는 다음과 같이 특정 키가 있는지 확인한 뒤 해당 기능을 만든다.

def personal_info(**kwargs):
    if 'name' in kwargs:    # in으로 딕셔너리 안에 특정 키가 있는지 확인
        print('이름: ', kwargs['name'])
    if 'age' in kwargs:
        print('나이: ', kwargs['age'])
    if 'address' in kwargs:
        print('주소: ', kwargs['address'])

% 고정 인수와 가변 인수(키워드 인수)를 함께 사용하기

고정 인수와 가변 인수를 같이 사용할 때는 다음과 같이 고정 인수를 먼저 지정하고 그 다음에 가변 인수를 마지막에 지정해준다.

>>> def personal_info(name, **kwargs):
...     print(name)
...     print(kwargs)
...
>>> personal_info('홍길동')
홍길동
{}
>>> personal_info('홍길동', age=30, address='서울시 용산구 이촌동')
홍길동
{'age': 30, 'address': '서울시 용산구 이촌동'}
>>> personal_info(**{'name': '홍길동', 'age': 30, 'address': '서울시 용산구 이촌동'})
홍길동
{'age': 30, 'address': '서울시 용산구 이촌동'}

이때 `def personal_info(kwargs, name):와 같이 고정 인수 앞에 가변 인수가 오면 안된다. 매개변수 순서에서kwargs` 는 반드시 맨 마지막에 와야한다.

% 위치 인수와 키워드 인수 같이 사용하기

함수에서는 위치 인수를 받는 *args 와 키워드 인수를 받는 **kwargs 를 함께 사용할 수 있다. 대표적인 함수가 print 인데 print 는 출력할 값을 위치 인수로 넣고 sep 이나 end 등을 키워드 인수로 넣는다.

>>> def custom_print(*args, **kwargs):
...     print(*args, **kwargs)
...
>>> custom_print(1, 2, 3, sep=':', end='')
1:2:3

이때 def custom_print(**kwargs, *args):처럼 **kwargs*args보다 앞쪽에 오면 안된다. 매개변수 순서에서 **kwargs는 반드시 가장 뒤쪽에 와야 한다.

특히 고정 매개변수와 *args, **kwargs를 함께 사용한다면 def custom_print(a, b, *args, **kwargs): 처럼 매개변수는 고정 매개변수, *args, **kwargs 순으로 지정해야 한다.

4. 매개변수에 초기값 지정

지금까지 함수를 호출할 때 항상 인수를 넣어서 전달하였다. 그렇다면 인수를 생략할 수는 없을까? 이때는 함수의 매개변수에 초기값을 지정해주면 된다.

>>> def personal_info(name, age, address='비공개'):
...     print('이름: ', name)
...     print('나이: ', age)
...     print('주소: ', address)
...

위와 같이 address='비공개' 라고 하여 address 의 초기값을 '비공개'로 지정하면

>>> personal_info('홍길동', 30)
이름:  홍길동
나이:  30
주소:  비공개

위와 같이 address 인수를 생략하면 초기값인 '비공개'가 들어가게 된다.

>>> personal_info('홍길동', 30, '서울시 용산구 이촌동')
이름:  홍길동
나이:  30
주소:  서울시 용산구 이촌동

매개변수의 초기값이 지정되어 있다고 하더라도 값을 넣으면 해당 값이 전달된다.

초기값이 지정되는 매개변수의 위치

매개변수의 초기값을 지정할 때 한 가지 주의할 점이 있다. 초기값이 지정된 매개변수 다음에는 초기값이 지정되어 있지 않은 매개변수가 오면 안된다.

>>> def personal_info(name, address='비공개', age):
...     print('이름: ', name)
...     print('나이: ', age)
...     print('주소: ', address)
...
  File "<stdin>", line 1
SyntaxError: non-default argument follows default argument

왜냐하면 이렇게 만들어버리면 personal_info('홍길동', 30) 이라고 하면 30이 어디로 들어가는지 알 수 없기 때문이다. address 로 들어가자니 age 가 비어버리게 된다. 이는 잘못된 문법으로 이렇게 만들어버리면 안된다.

즉, 다음과 같이 초기값이 지정된 매개변수는 뒤쪽으로 몰아주면 된다.

def personal_info(name, age, address='비공개'):
def personal_info(name, age=0, address='비공개'):
def personal_info(name='비공개', age=0, address='비공개'):

참고로 def personal_info(name='비공개', age=0, address='비공개'): 와 같이 초기값이 전부 지정되어 있으면 인수를 안넘기고 personal_info() 이런식으로 사용해도 함수를 호출할 수 있다.

지금까지 위치 인수, 키워드 인수, 매개변수의 초기값 사용 방법에 대해서 알아보았다. 함수에서 * 를 붙이거나 ** 를 붙이는 문법이 조금 생소할 수 있으나 리스트에 * 를 쓰고 딕셔너리에 ** 를 쓴다는 것만 알아두자

profile
벨로그보단 티스토리를 사용합니다! https://flight-developer-stroy.tistory.com/

0개의 댓글