inner function이 왜 좋을까? (ft. closure)

jj·2021년 7월 12일
0

python

목록 보기
3/3

inner functions (a.k.a nested functions)

파이토치를 공부하면서 다음과 같은 형식의 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)
...

Inner Functions 사용하기 : 기초편

  • 외부의 접근으로 부터 당신의 함수를 숨기기 위한 encapsulation이 가능
  • closure, decorator 등이 가능

Providing Encapsulation

  • 가장 보편적으로 함수를 보호하거나 숨길 때 사용

  • 글로벌 스코프에서 완전하게 숨길 수 있다

    >>> 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

Building Helper Inner Functions

  • 같은 뭉탱이의 코드를 반복하는 다른 함수가 존재할 수 있다.
  • Sometimes you have a function that performs the same chunk of code in several places within its body. For example, say you want to write a function to process a CSV file containing information about the Wi-Fi hotspots in New York City. To find the total number of hotspots in New York as well as the company that provides most of them, you create the following script:

Retaining State With Inner Functions: Closures

파이썬에서는, 함수는 first class citizen이다.

-> 그들은 오브젝트, 숫자, 문자열, 리스트, 튜플, 모듈등과 동등하다는 것이다. 또한 데이터구조에 동적으로 저장하거나 삭제할 수 있고, 다른 함수에게 argument로 전달하거나 return 값으로 줄 수 있다.

또한 higher-order function 을 만들 수 도 있다

-> 다른 함수를 인수로 받아들이거나 반환하거나 둘 다로 수행하는 함수를 의미한다.

closure factory functions의 가장 큰 특징은 클로져가 생성된 로컬 네임스페이스의 변수와 이름에 완전하게 접근이 가능하다는 것이다. 그리고 이는 enclosing functoin이 리턴되거나 실행을 끝내도 가능하다. -> retaining state between function calls.

Retaining State in a Closure

클로저는 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()를 호출하면, 다음과 같은 상황이 벌어진다.

  1. power()함수의 새 인스턴스를 정의: base라는 하나의 인자만을 받는 함수
  2. power()함수의 현재 상태를 스냅샷으로 찍는다. 이때, exponent 의 현재값도 같이 저장한다.
  3. power() 를 리턴한다. 이때, 모든 관련상태도 함께 반환한다.

이러한 방식으로, generate_power()의 리턴인 power()의 인스턴스를 호출할 때 , exponent 값을 기억하고 있다는 것을 알 수 있다.

profile
재밌는게 재밌는거다

0개의 댓글