파이썬 함수에서 위치 인수, 키워드 인수, 재귀 호출 사용

Yeonu·2020년 11월 21일
0

Python 이론

목록 보기
17/30
post-thumbnail
  • 매개변수와 인수🤔
    매개변수 : 함수 바깥에서 전달받은 값이 저장되는 변수
    def add(a, b):    # a와 b가 매개변수
        return a + b
    인수 : 함수를 호출할 때 전달하는 값이나 변수
    add(10, 20)    # 10과 20이 인수



위치 인수와 리스트 언패킹 사용하기

함수에 인수를 순서대로 넣는 방식을 위치 인수(positional argument)라고 한다. 즉, 인수의 위치가 정해져 있다.

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



위치 인수를 사용하는 함수를 만들고 호출하기

>>> def print_numbers(a, b, c):
...     print(a)
...     print(b)
...     print(c)
>>> print_numbers(10, 20, 30)
10
20
30



언패킹 사용하기

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

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

# 변수 대신 리스트 바로 앞에 붙여도 된다.
>>> print_numbers(*[10, 20, 30])
10
20
30

단, 이때 함수의 매개변수 개수와 리스트의 요소 개수는 같아야 한다.



가변 인수 함수 만들기

위치 인수와 리스트 언패킹은 인수의 개수가 정해지지 않은 가변 인수(variable argument)에 사용한다. 즉, 같은 함수에 인수 한 개를 넣을 수도 있고, 열 개를 넣을 수도 있다. 또는, 인수를 넣지 않을 수도 있다.

가변 인수 함수는 매개변수 앞에 *를 붙여서 만든다.

>>> def print_numbers(*args):
...     for arg in args:
...         print(arg)
...

고정 인수와 가변 인수를 함께 사용할 때는 다음과 같이 고정 매개변수를 먼저 지정하고, 그 다음 매개변수에 *를 붙여주면 된다. 가변 인수가 고정 매개변수보다 앞쪽에 오면 안된다.

>> 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 personal_info(name, age, address):
...     print('이름: ', name)
...     print('나이: ', age)
...     print('주소: ', address)
...
>>> personal_info('홍길동', 30, '서울시 용산구 이촌동')
이름:  홍길동
나이:  30
주소:  서울시 용산구 이촌동
  ```
>>> personal_info(age=30, address='서울시 용산구 이촌동', name='홍길동')
이름:  홍길동
나이:  30
주소:  서울시 용산구 이촌동

print 함수에서 사용했던 sep, end도 키워드 인수다.

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



키워드 인수와 딕셔너리 언패킹 사용하기

함수를 호출할 때 키워드 인수로 직접 값을 넣었다. 이번에는 딕셔너리를 사용해서 키워드 인수로 값을 넣는 딕셔너리 언패킹을 사용해보겠다. 다음과 같이 딕셔너리 앞에 **(애스터리스크 두 개)를 붙여서 함수에 넣어준다.

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

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

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

# 딕셔너리 변수 대신 딕셔너리 앞에 바로 **를 붙여도 동작은 같다.
>>> personal_info(**{'name': '홍길동', 'age': 30, 'address': '서울시 용산구 이촌동'})
이름:  홍길동
나이:  30
주소:  서울시 용산구 이촌동

딕셔너리 언패킹을 사용할 때는 함수의 매개변수 이름과 딕셔너리의 키 이름이 같아야 한다. 매개변수 개수와 딕셔너리 키의 개수도 같아야 한다.



**를 두 번 사용하는 이유

딕셔너리는 키-값 쌍 형태로 저장되어 있어 애스터리스크를 한 번 사용하면 키가 출력된다. 즉, 딕셔너리를 한 번 언패킹하면 키를 사용한다는 뜻이다. 따라서 애스터리스크를 두번 사용해 딕셔너리를 두 번 언패킹하여 값을 사용한다.



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

키워드 인수를 사용하는 가변 인수 함수는 매개변수 앞에 **를 붙여서 만든다.
아래는 값 여러 개를 받아서 매개변수 이름과 값을 각 줄에 출력하는 함수다. 함수를 만들 때 괄호 안에 **
kwargs와 같이 매개변수 앞에 **를 붙인다. 함수 안에서는 for로 kwargs.items()를 반복하면서 print로 값을 출력한다.

>>> def personal_info(**kwargs):
...     for kw, arg in kwargs.items():
...         print(kw, ': ', arg, sep='')
...
>>> 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: 서울시 용산구 이촌동



매개변수에 초깃값 지정하기

매개변수의 초깃값은 주로 사용하는 값이 있으면서 가끔 다른 값을 사용해야 할 때 활용한다. 대표적인 예가 print 함수인데, print 함수의 sep는 초깃값이 ' '(공백)으로 지정되어 있어서 대부분 그대로 사용하고, 가끔 sep에 다른 값을 넣어서 사용한다.

# 매개변수 address의 초깃값 지정
>>> def personal_info(name, age, address='비공개'):
...     print('이름: ', name)
...     print('나이: ', age)
...     print('주소: ', address)

# 실행 결과
# address는 초깃값이 있으므로 personal_info는 다음과 같이 address 부분을 비워 두고 호출할 수 있다.
>>> personal_info('홍길동', 30)
이름:  홍길동
나이:  30
주소:  비공개

# 매개변수에 초깃값이 지정되어 있더라도 값을 넣으면 해당 값이 전달된다.
>>> personal_info('홍길동', 30, '서울시 용산구 이촌동')
이름:  홍길동
나이:  30
주소:  서울시 용산구 이촌동



초깃값이 지정된 매개변수의 위치

초깃값이 지정된 매개변수 다음에는 초깃값이 없는 매개변수가 올 수 없다. 초깃값이 지정된 매개변수는 뒤쪽에 몰아주면 된다.

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



@심사문제 30.7

korean, english, mathematics, science = map(int, input().split())

def get_min_max_score(*args):
   return min(args),max(args)
def get_average(**kwargs):
   return (sum(kwargs.values()))/len(kwargs)

min_score, max_score = get_min_max_score(korean, english, mathematics, science)
average_score = get_average(korean=korean, english=english,
                           mathematics=mathematics, science=science)
print('낮은 점수: {0:.2f}, 높은 점수: {1:.2f}, 평균 점수: {2:.2f}'
     .format(min_score, max_score, average_score))

min_score, max_score = get_min_max_score(english, science)
average_score = get_average(english=english, science=science)
print('낮은 점수: {0:.2f}, 높은 점수: {1:.2f}, 평균 점수: {2:.2f}'
     .format(min_score, max_score, average_score))

get_min_max_score 함수는 get_min_max_score(korean, english, mathematics, science)처럼 인수를 위치 인수로 넣고 있으므로 def get_min_max_score(*args):와 같이 만들어준다.

get_average 함수는 get_average(korean=korean, english=english, mathematics=mathematics, science=science)처럼 인수를 키워드 인수로 넣고 있으므로 def get_average(**kwargs):와 같이 만들어준다. 함수 안에서는 평균을 구해야 하는데 먼저 sum(kwargs.values())처럼 values로 딕셔너리의 값만 가져온 뒤 sum으로 합계를 구한다.





함수에서 재귀호출 사용하기

함수 안에서 함수 자기자신을 호출하는 방식을 재귀호출(recursive call)이라고 한다.

def hello():
   print('Hello, world!')
   hello()

hello()

실행해보면 'Hello, world!' 문자열이 계속 출력되다가 에러가 발생한다. 파이썬에서는 최대 재귀 깊이(maximum recursion depth)가 1,000으로 정해져 있어서 hello 함수가 자기자신을 계속 호출하다가 최대 재귀 깊이를 초과하면 RecursionError가 발생한다.



재귀호출 종료 조건 만들기

def hello(count):
   if count == 0:    # 종료 조건을 만듦. count가 0이면 다시 hello 함수를 호출하지 않고 끝냄
       return
   print('Hello, world!', count)
   count -= 1      # count를 1 감소시킨 뒤
   hello(count)    # 다시 hello에 넣음
hello(5)    # hello 함수 호출

#실행결과
Hello, world! 5
Hello, world! 4
Hello, world! 3
Hello, world! 2
Hello, world! 1



재귀호출로 팩토리얼 구하기

팩토리얼은 1부터 n까지 양의 정수를 차례대로 곱한 값이며 !(느낌표) 기호로 표기한다. 예를 들어 5!은 5 4 3 2 1이며 결과는 120이다.
재귀호출은 반환값이 중요하다. 반환값이 없으면 무한정 돌다 에러 발생

def factorial(n):
   if n == 1:      # n이 1일 때
       return 1    # 1을 반환하고 재귀호출을 끝냄
   return n * factorial(n - 1)  # n과 factorial 함수에 n - 1을 넣어서 반환된 값을 곱함

print(factorial(5))


마지막 1이 반환됨으로써 1이 상위 값들과 연산하며 최종값이 다시 반환된다.



@연습문제 31.4

https://dojang.io/mod/page/view.php?id=2355

  ```
def is_palindrome(word):
    if len(word) < 2: # 문자가 두 개 미만이면 True반환. 종료
        return True
    if word[0] != word[-1]: # 제일 바깥쪽 문자열 쌍 비교
        return False
    return is_palindrome(word[1:-1]) # 밖에서 두번째 문자열 쌍까지만
    				# (제일 바깥쪽 문자열 쌍 제외) 잘라서 다시 비교
print(is_palindrome('hello'))
print(is_palindrome('level'))

  #실행 결과
  False
  True
```

if len(word) < 2:는 if word[0] != word[-1]:보다 먼저 와야 한다. 그렇지 않으면 글자가 하나도 없는데도 word[0]과 word[-1]에 접근하여 에러가 발생하게 된다.


@심사문제 31.5

https://dojang.io/mod/quiz/review.php?attempt=1154493&cmid=2356
피보나치 수열 : 바로 앞의 두 피보나치 수의 합

반환값 1이 나올 때까지 계속 돈다.

하민님이 알려주신 좋은 재귀함수 강의!
CS Dojo 동적코딩

0개의 댓글