파이토치를 공부하면서 다음과 같은 형식의 inner function을 마주하게 되었다. 예전에 수업에서 decorator를 공부했을 때도 비슷한 형식의 wrapper 함수를 정의했던 적이 있었다. 그때는 그런가보다 하고 넘겼는데, 왜 이런 inner function이 쓰이는지 짚고 넘어가보려고 한다.
https://realpython.com/inner-functions-what-are-they-good-for/ 를 참고하여 작성하였다.
일단 inner function의 구조는 다음과 같다:
def outer_func():
def inner_func():
print('inner~~')
inner_func()
inner function의 가장 큰 특징은 자신의 enclosing function 의 variable과 object를 자유롭게 접근할 수 있다는 것이다. 심지어 함수가 return되었다고 할지라도. enclosing function은 inner function이 접근할 수 있는 namespace를 제공한다.
def outer_func(agrs):
def inner_func():
print(f'{args} in inner~~')
inner_func()
위의 코드 처럼, outer_func()에 전달된 문자열 argument args
는 inner_func()가 args
라는 이름으로 접근할 수 있다. 그러나 이 args
라는 이름은 outer_func()의 local scope으로 정의되어있다. 이렇게 바깥쪽 함수의 로컬 스코프에서 정의된 이름을 nonlocal name이라고 한다. 안쪽 함수의 입장에서는 nonlocal하기 때문.
Advantages:
모든 argument를 outer function에서 체크하기 때문에, inner function에서 에러를 skip할 수 있고, 연산에 집중할 수 있게 된다
이런 느낌처럼
def factorial(number):
... # Validate input
... if not isinstance(number, int):
... raise TypeError("Sorry. 'number' must be an integer.")
... if number < 0:
... raise ValueError("Sorry. 'number' must be zero or positive.")
... # Calculate the factorial of number
... def inner_factorial(number):
... if number <= 1:
... return 1
... return number * inner_factorial(number - 1)
... return inner_factorial(number)
...
가장 보편적으로 함수를 보호하거나 숨길 때 사용
글로벌 스코프에서 완전하게 숨길 수 있다
>>> def increment(number):
... def inner_increment():
... return number + 1
... return inner_increment()
...
>>> increment(10)
11
>>> # Call inner_increment()
>>> inner_increment()
Traceback (most recent call last):
File "<input>", line 1, in <module>
inner_increment()
NameError: name 'inner_increment' is not defined
파이썬에서는, 함수는 first class citizen이다.
-> 그들은 오브젝트, 숫자, 문자열, 리스트, 튜플, 모듈등과 동등하다는 것이다. 또한 데이터구조에 동적으로 저장하거나 삭제할 수 있고, 다른 함수에게 argument로 전달하거나 return 값으로 줄 수 있다.
또한 higher-order function 을 만들 수 도 있다
-> 다른 함수를 인수로 받아들이거나 반환하거나 둘 다로 수행하는 함수를 의미한다.
closure factory functions의 가장 큰 특징은 클로져가 생성된 로컬 네임스페이스의 변수와 이름에 완전하게 접근이 가능하다는 것이다. 그리고 이는 enclosing functoin이 리턴되거나 실행을 끝내도 가능하다. -> retaining state between function calls.
클로저는 inner function으로 하여금 그것의 환경 상태를 유지하도록 한다. 클로저는 inner function 그자체를 의미하는 것이 아니라, enclosing 환경을 동반하고 있는 inner function을 의미한다.
def generate_power(exponent):
def power(base):
return base ** exponent
return power
power()
함수가exponent
인자를 받아오는 곳은 클로져가 활동을 시작하는 부분이다. 위의 예시에서는 power()
함수가 바깥 함수generate_power()
로부터 exponent
값을 받아온다. generate_power()
를 호출하면, 다음과 같은 상황이 벌어진다.
power()
함수의 새 인스턴스를 정의: base
라는 하나의 인자만을 받는 함수power()
함수의 현재 상태를 스냅샷으로 찍는다. 이때, exponent
의 현재값도 같이 저장한다.power()
를 리턴한다. 이때, 모든 관련상태도 함께 반환한다.이러한 방식으로, generate_power()
의 리턴인 power()
의 인스턴스를 호출할 때 , exponent
값을 기억하고 있다는 것을 알 수 있다.