본문 바로가기
Python/Basic

[파이썬, Python] 클로저(Closure)와 데코레이터(Decorator)

by coding-choonsik 2023. 3. 14.
728x90
반응형
SMALL

1. 클로저(Closure)

  • 함수 안의 함수를 결과로 반환할 때, 그 내부 함수를 클로저라고 함
  • 콜백함수, 함수의 순차적 실행, 데코레이터 함수에 사용

설명이 어려우니 코드로 이해해보자!


📍 숫자 n을 매개변수로 받고  2 ~ 9를 곱해주는 함수를 만들어보자.

def mul2(n):
  return n * 2
  
mul2(10)
>>> 20
mul2(5)
>>> 10


def mul5(n):
  return n * 5
  
mul5(10)
>>> 50

mul5(5)
>>> 25

 

 

이렇게 함수를 하나씩 꼭 만들어야 할까..?😢 class로 만들어보자!

class를 만들긴 했지만 매번 객체를 생성하여야 한다는 단점이 있다.

class Mul:
  def __init__(self, m):      # 매개변수 숫자를 입력받음
    self.m = m				  # 개체 생성시 입력되는 매개변수 초기화

  def mul(self, n):			  
    return self.m * n
    
mul2 = Mul(2)    			# 객체 생성, 생성자 m = 2

print(mul2.mul(10))			# 2 * 10
>>> 20				
print(mul2.mul(5))
>>> 10						# 2 * 5

mul5 = Mul(5)				# 객체 생성, 생성자 m = 5

print(mul5.mul(10))	
>>> 50
print(mul5.mul(5))
>>> 25

 

스페셜 메소드를 사용하여 객체가 실행되면 자동으로 호출되는 함수를 만들어보자.

class Mul:
  def __init__(self, m):    # 객체 생성할 때 자동으로 호출
    print('생성자 호출!')
    self.m = m

  def __call__(self, n):    # 스페셜 메소드 사용, 객체를 실행할 때 자동으로 호출
    print('call 호출!')
    return self.m * n		
    
mul2 = Mul(2)      		 	# 객체 생성
>>> 생성자 호출!

mul2(10) 					# 객체를 실행할 때 call 메소드호출
>>> call 호출!
20

mul5 = Mul(5) 				# 객체 생성
>>> 생성자 호출!

mul5(10)					# 객체를 실행할 때 call 메소드 호출
>>> 50

 

✅ 클로저 사용하기!

def mul(m):         # 외부함수
  def wrapper(n):   # 내부 함수(클로저)
    return m * n
  return wrapper
  
mul2 = mul(2)     # m = 2인 wrapper 함수가 mul2에 저장
print(mul2(10))   # m = 2, n = 10인 wrapper 함수가 실행
>>> 20

mul5 = mul(5)	  # m = 5인 wrapper함수가 mul5에 저장
print(mul5(10))	  # m = 5, n = 10인 wrapper 함수가 실행
>>> 50

2. 데코레이터(Decorator)

  • 함수를 꾸며주는 함수
  • 함수를 인수로 받는 클로저
  • @(어노테이션)을 이용하여 사용
  • 반복되는 작업을 여러 함수에 적용할 경우, 기존 함수를 수정하지 않고 추가 기능을 구현하고 싶은 경우

✅ 매개변수로 숫자 2개를 받아 두 수를 더하고 곱하는는 함수를 만들고 함수가 수행되는 시간을 알아보자.

import time     

# 더하기
def func1(a, b):
  start = time.time()        			# 현재 시간을 ms 단위까지 나타냄
  print('함수가 시작되었습니다')

  result = a + b

  end = time.time()
  print(f'함수 수행시간: {end - start}')    # 함수가 수행될 때부터 출력할 때 까지 소요되는 시간(ms) 계산

  return result
  
result = func1(10,3)    # 10 + 3
print(result)
>>> 함수가 시작되었습니다
함수 수행시간: 5.078315734863281e-05
13

# 곱하기
def func2(a, b):
  start = time.time()
  print('함수가 시작되었습니다')

  result = a * b

  end = time.time()
  print(f'함수 수행시간: {end - start}')

  return result
  
result = func2(10,3)    # 10 * 3
print(result)
>>> 함수가 시작되었습니다
함수 수행시간: 0.0038857460021972656
30

✅데코레이터를 만들어 클로저를 이용하여 구현해보자!'

# 데코레이터 만들기
def func1(a, b):
  result = a + b
  return result

def func2(a, b):
  result = a * b
  return result
  
def elapsed(func):       # 관례적으로 elapsed 사용
  def wrapper(a, b):     # 클로저 이용
    start = time.time()
    print('함수가 시작되었습니다')

    result = func(a, b)   # 함수에 전달된 a, b를 실행

    end = time.time()
    print(f'함수 수행시간: {end - start}')
    return result
  return wrapper
  
deco1 = elapsed(func1)    # 외부함수
result = deco1(10, 3)
print(result)
>>> 함수가 시작되었습니다
함수 수행시간: 0.000986337661743164
13

deco2 = elapsed(func2)
result = deco2(10, 3)
print(result)
>>> 함수가 시작되었습니다
함수 수행시간: 0.0010008811950683594
30

✅ 위 코드를 어노테이션(@)을 이용하여 구현하기

def elapsed(func):
  def wrapper(a, b):     # 클로저 이용
    start = time.time()
    print('함수가 시작되었습니다')

    result = func(a, b)   # 함수에 전달된 a, b를 실행

    end = time.time()
    print(f'함수 수행시간: {end - start}')
    return result
  return wrapper

@elapsed    
def func1(a, b):
  result = a + b
  return result

@elapsed
def func2(a, b):
  result = a * b
  return result

result = func1(10,3)    # elapsed함수를 실행하여 담을 변수를 별도 만들지 않음
print(result)
>>> 함수가 시작되었습니다
함수 수행시간: 0.0006263256072998047
13

result = func2(10,3)
print(result)
>>> 함수가 시작되었습니다
함수 수행시간: 0.0008640289306640625
30

 

728x90
반응형
LIST