기록과 정리의 공간

[Python] 클로저(Closure Function) 본문

언어/Python

[Python] 클로저(Closure Function)

딸기맛도나쓰 2020. 9. 3. 23:25

1. 중첩 함수(Nested Function)

  • 함수 내부에 정의된 또 다른 함수

  • 중첩 함수는 해당 함수가 정의된 함수 내에서 호출 및 리턴이 가능하다.

  • 함수 안에 선언된 변수(로컬 변수)는 함수 안에서만 사용 가능한 원리와 동일하다.

    • 예시
def outer_func():
    print("outer_func 호출됨")
    def inner_func():
        return "inner_func 호출됨"
    print(inner_func())

outer_func()

"""  
결과값 :  
outer_func 호출됨  
inner_func 호출됨  
"""
  • 중첩 함수를 함수 밖에서도 호출 할 수 있는 방법이 있다. (자세한 것은 2, 3번 참고)
def outer_func(num):  
    def inner_func():  
        print(num)  
        return "inner_func 호출 끝"  
    return inner_func

func = outer_func(10) # First-class function  
print(func()) # Closure 호출

"""  
결과값:  
10  
inner_func 호출 끝  
"""

2. First-class 함수란?

  • First-class함수란 아래와 같이 프로그래밍 언어가 함수를 first-class citizen으로 취급하는 것을 뜻 한다.
    • 함수를 변수에 저장 할 수 있다.
    • 함수의 인자에 다른 함수를 전달 할 수 있다.
    • 함수의 리턴값으로 함수를 전달 할 수 있다.
  • python에서는 모든 것이 객체이므로, python의 함수는 First-class함수로 사용이 가능하다.
  • 참고로 이것은 함수형 프로그래밍에서 고안된 기법이다. python, GO, javascript, kotlin은 이를 지원한다.(c언어 등은 미지원)
  • 예시1 : 함수를 변수에 할당하기
def calc_plus(n):  
    return n + n

func1 = calc_plus # func1이라는 변수에 위의 함수를 할당할 수 있다.  
print(func1) # 결과값 : <function calc_plus at 0x0000024C2AD75310> (calc_plus함수를 가리킴)  
print(func1(2)) # 결과값 : 4
  • 예시2 : 함수를 다른 함수의 인자로 전달하기
def calc_plus(n):  
    return n + n

def calc_square(n):  
    return n * n

def calc_quad(n):  
    return n * n * n * n

def list_calc(func, num_list):  
    result = []  
    for num in num_list:  
        result.append(func(num))  
    print(result)

num_list = [1, 2, 3, 4]  
list_calc(calc_plus, num_list) # 결과값 : [2, 4, 6, 8]  
list_calc(calc_square, num_list) # 결과값 : [1, 4, 9, 16]  
list_calc(calc_quad, num_list) # 결과값 : [1, 16, 81, 256]
  • 예시3 : 함수의 리턴값으로 함수를 전달하기
def logger(msg):  
    def msg_creator():  
        print('생성 메시지 -', msg)  
    return msg_creator # 리턴값으로 함수 전달하기

func = logger("안녕하세요.")  
func() # 결과값 : 생성 메시지 - 안녕하세요.
  • First-class 함수 활용 예시
def html_creator(tag):  
    def text_wrapper(msg):  
        print ('<{0}>{1}</{0}>'.format(tag, msg))  
    return text_wrapper

h1_tag_creator = html_creator('h1')  
h1_tag_creator("안녕") # 결과값 : <h1>안녕</h1>

p_tag_creator = html_creator('p')  
p_tag_creator("안녕") # 결과값 : <p>안녕</p>
  • 연습 문제1 : 아래와 같은 결과값이 출력되도록 First-class함수를 만들어보자.
# 결과값

- first
- second
- third
# 정답코드

def sign_creator(sign):
    def input_text(text):
        print('{0} {1}'.format(sign, text))
    return input_text

hypen_creator = sign_creator('-')
text_list = ['first', 'second', 'third']

for text in text_list:
    hypen_creator(text)
  • 연습 문제2 : 크롤링을 이용해 링크의 '취미로 익히는 IT' 부분의 목록을 아래와 같은 형태로 출력해보자.
1. (왕초보) - 클래스 소개
2. (왕초보) - 블로그 개발 필요한 준비물 준비하기
3. (왕초보) - Github pages 설정해서 블로그 첫 페이지 만들어보기
4. (왕초보) - 초간단 페이지 만들어보기
5. (왕초보) - 이쁘게 테마 적용해보기
6. (왕초보) - 마크다운 기초 이해하고, 실제 나만의 블로그 페이지 만들기        
7. (왕초보) - 다양한 마크다운 기법 익혀보며, 나만의 블로그 페이지 꾸며보기 
import requests
from bs4 import BeautifulSoup

res = requests.get("https://davelee-fun.github.io/blog/crawl_html_css.html")
soup = BeautifulSoup(res.content, 'html.parser')
lecture_lists = soup.select('ul#hobby_course_list li')

def add_nums(num):
    def get_lists(lists):
        print("{0}. {1}".format(num, lists))
    return get_lists

for i, list in enumerate(lecture_lists, start=1):
    func = add_nums(i)
    func(list.get_text())

3. Closure Function (클로저)

  • 함수와 해당 함수가 가지고 있는 데이터를 함께 복사, 저장해서 별도의 함수로 사용하는 기법으로 First-class함수와 동일하다.
  • 외부 함수가 소멸되더라도, 외부 함수 내의 로컬 변수 값과 내부 함수(중첩 함수)를 사용할 수 있는 기법이다.
  • 클로저를 사용하는 경우
    • 클로저는 객체와 유사하다.
    • 일반적으로 제공해야할 기능(메소드)가 적은 경우, 클로저를 사용하기도 한다.
    • 제공해야할 기능이 많은 경우에는 class를 이용하여 구현한다.
  • 자세한 것은 예제 코드를 보도록 하자.
    • 아래 코드에서 closure_func가 클로저이다.
    • #1에서 outer_func()는 호출이 종료된다.
    • #2에서 closure_func()는 inner_func함수를 호출한다.
      #1에서 외부 함수 호출이 종료되면서 n값은 사라지나, #2에서 closure_func()에서 inner_func가 호출 되면서 이전의 n값인 4를 사용한다.
    • 심지어 #3에서 외부 함수를 삭제해도 inner_func()와 n값은 살아있는 것을 결과값을 통해 확인할 수 있다.
def outer_func(n):
    def inner_func():
        print(n)
        return "안녕"
    return inner_func

closure_func = outer_func(4) #1 First-class 함수  
print(closure_func()) #2 Closure 호출

print("-----")

del outer_func #3  
print(closure_func())

"""  
결과값:  
4  
안녕
---
4  
안녕  
"""
  • 예시
def multiply(n):
    def power(nums):
        return nums ** n
    return power

power2 = multiply(2)
power3 = multiply(3)
power4 = multiply(4)

print(power2(2)) # 4
print(power3(2)) # 8
print(power4(2)) # 16
  • 연습 문제 : 위 예시의 multipy()를 사용해서 아래와 같은 결과값을 출력해보자.
# 결과값
1의 1승 = 1
1의 2승 = 1
1의 3승 = 1
1의 4승 = 1
1의 5승 = 1
---------  
2의 1승 = 2
2의 2승 = 4
2의 3승 = 8
2의 4승 = 16
2의 5승 = 32
---------
3의 1승 = 3
3의 2승 = 9
3의 3승 = 27
3의 4승 = 81
3의 5승 = 243
---------
4의 1승 = 4
4의 2승 = 16
4의 3승 = 64
4의 4승 = 256
4의 5승 = 1024
---------
5의 1승 = 5
5의 2승 = 25
5의 3승 = 125
5의 4승 = 625
5의 5승 = 3125
# 정답 코드

def multiply(n):
    def power(nums):
        return nums ** n
    return power

power_list = []
for i in range(1, 5+1):
    power_list.append(multiply(i))

for i in range(1, 5+1):
    if i >= 2:
        print("---------")
    for j in range(0, len(power_list)):
        print("{}의 {}승 = {}".format(i, j+1, power_list[j](i)))
Comments