이번 포스팅에서는 Python의 함수 중에서도 '함수 안의 함수 (Function in Function)' 로서 


- (1) 중첩 함수 (Nested Function): 함수 안에 정의된 함수

- (2) 재귀 함수 (Recursive Function): 함수 안에 자기 자신을 호출하는 함수


에 대해서 알아보겠습니다. 


[ Python 함수 안의 함수 (Function in Function) ]




  (1) 중첩 함수 (Nested Function): 함수 안에 정의된 함수


def 로 시작하는 함수 안에 또 다른 하나의 def 로 시작하는 함수를 정의할 수 있는데요, 이때 함수 안에 정의된 또 다른 함수를 중첩 함수 (Nested Function) 이라고 합니다. 


중첩 함수는 자기가 속한 원래 함수의 매개변수를 받아서 사용할 수 있습니다. 아래의 예시는 outer() 함수의 num 매개변수를 중첩함수인 inner() 함수의 매개변수 input 으로 사용해서 최종 결과값인 result2 를 반환하도록 하는 함수입니다. 



In [1]: def outer(num):

   ...:

   ...:     def inner(n): # nested function

   ...:         result1 = n + 1

   ...:         return result1

   ...:

   ...:     result2 = inner(num)**2

   ...:     return result2;


In [2]: outer(9) # (9+1)**2

   ...:

Out[2]: 100

 




중첩 함수 (nested function)는 자신이 속한 원래 함수 안에서만 역할을 하며, 원래 함수의 밖에서는 인식이 안됩니다.  아래의 [3] 처럼 중첩함수인 inner() 함수를 호출하려고 하면 'NameError: name 'inner' is not defined' 라는 에러메시지가 뜹니다. 



In [3]: inner(10) # NameError

Traceback (most recent call last):


File "<ipython-input-3-4050b378a5f5>", line 1, in <module>

inner(10) # NameError


NameError: name 'inner' is not defined

 




중첩함수를 사용하면 복잡한 수식도 여러개의 함수를 하나의 함수로 묶어서 정의할 수 있습니다. 아래의 예시는 모집단의 표준편차(population standard deviation) 사용자 정의 함수를 중첩함수를 사용해서 정의한 것입니다. 





In [4]: import math


In [5]: def stddev(*args):

   ...:

   ...:     def mean_func():  # nested function

   ...:         mean = sum(args)/len(args)

   ...:         return mean

   ...:

   ...:     def variance(mean):  # nested function

   ...:         squared_deviation = 0

   ...:         for arg in args:

   ...:             squared_deviation += (arg - mean)**2

   ...:             var = squared_deviation / len(args)

   ...:         return var

   ...:

   ...:     v = variance(mean_func())

   ...:     return math.sqrt(v)


In [6]: stddev(1.0, 2.0, 3.0, 4.0, 5.0)

Out[6]: 1.4142135623730951



# standard deviation using numpy std()

In [7]: import numpy as np


In [8]: np.std([1.0, 2.0, 3.0, 4.0, 5.0])

Out[8]: 1.4142135623730951

 




  (2) 재귀 함수 (Recursive Function): 함수 안에 자기 자신을 호출하는 함수


재귀 함수(Recursive Function)는 함수 안에서 자기 자신을 호출하는 함수를 말합니다. 




함수가 호출자이자 동시에 피호출자가 되어서 반복에 반복에 반복을 계속하는 함수입니다. 



왼쪽의 사진을 보면 재귀함수가 자기 자신을 계속해서 반복하면서 호출을 하는 것이 무엇을 의미하는지 이해가 될 것 같습니다. 


(*출처: http://www.itcuties.com/java/recursion-and-iteration/)












재귀함수(recursive function)와 중첩함수(nested function)를 같이 이용해서 Factorial 계산하는 함수를 정의해보겠습니다. 



In [9]: def factorial(number):

   ...:

   ...: # error handling

   ...:     if not isinstance(number, int):

   ...:         raise TypeError("Oops. 'number' must be an integer.")

   ...:     if number < 0:

   ...:         raise ValueError("Oops. 'number' must be zero or positive.")

   ...:

   ...:     def inner_factorial(number): # nested function

   ...:         if number == 0:

   ...:             return 1

   ...:         elif number > 0:

   ...:             return inner_factorial(number-1)*number # recursive function

   ...:

   ...:     return inner_factorial(number)


In [10]: factorial(5)

Out[10]: 120

 



위의 factorial(5) 함수를 실행하면 아래에 각 절차를 풀어서 설명해 놓은 것과 같이 자기 자신의 factorial() 함수를 계속 반복해서 호출하면서 값을 계산해서 최종값을 반환하게 됩니다. 




재귀함수는 편리한 점도 있지만 호출 비용이 크므로 성능이 중요한 경우에는 재귀함수 대신 반복문을 사용하는 것이 좋겠습니다.  그리고 재귀함수를 짤 때는 반드시 종료 조건을 정의해 주어야 무한반복의 함정에 빠지지 않습니다. (파이썬이 지정해 놓은 재귀 함수 반복 호출 최대 회수를 초과하면 'RuntimeError: maximum recursion depth exceeded while calling a Python object' 에러가 발생합니다)


많은 도움이 되었기를 바랍니다. 

Posted by R Friend R_Friend