Python 데코레이터

Chanho Yoon·2021년 4월 17일
2

Python

목록 보기
1/1

파이썬 데코레이터

1차(마켓컬리), 2차(에어비엔비) 프로젝트를 하면서 백엔드와 소통할 때 어렴풋이 데코레이터라는 용어를 들었는데 이번에 Flask 기술을 익히는데 개념이 나와서 알아보도록 하겠다.

데코레이터의 이해

nested function(중첩함수)

  • 함수안에 함수를 사용 (로컬 변수)
  • 중첩함수는 해당하는 함수 안에서만 사용 가능하고 함수 밖에서 호출 할 수 없다.

하지만 중첩함수를 밖에서 호출하는 방법이 있다

def fnc1 ():
	def fnc2():
    	...
    return fnc2

와 같이 함수 밖에서 사용할 수 있다.

First-class function(일급함수)

  • 함수 자체를 인자(Argument)로 다른 함수에 전달 할 수 있다
  • 다른 함수의 결과 값으로 리턴이 가능
  • 함수를 변수에 할당하거나 데이터 구조안에 저장할 수 있는 함수

파이썬은 "모든 것이 객체이다" 그래서 파이썬은 함수도 모두 객체로 되어 있어서 함수 기능 이외에 객체와 같이 활용이 가능하다. 그래서 파이썬의 함수는 First-class 함수로 사용 가능하다

def outer_fnc(tag):
  def inner_fnc(text):
    print('{0} {1}'.format(tag, text))
  return inner_fnc

test1 = outer_fnc('-')
test1('변수에 함수를 할당')
test2 = outer_fnc('*')
test2('해서 사용 가능하다!')

Closure function

  • 자신을 둘러싼 스코프(네임스페이스)의 상태값을 기억하는 함수이다.
  • 클로저는 일반 함수와는 다르게 자신의 영역 밖에서 호출된 함수의 변수값과 레퍼런스를 복사, 저장, 접근을 가능하게 한다.

클로저를 만족하는 세 가지 조건

  1. 해당 함수는 어떤 함수 내의 중첩된 함수여야 한다.
  2. 해당 함수는 자신을 둘러싼 함수 내의 상태값을 반드시 참조해야 한다.
  3. 해당 함수를 둘러싼 함수는 이 함수를 반환해야 한다.

클로저 함수를 사용해서 n승 출력해보기

calc_num = int(input('Number : '))
start_n = int(input('n승 시작 값 : '))
end_n = int(input('n승 마지막 값 : '))

def calc(num):
  def digit(digit):
    return digit ** num
  return digit

list_data = list()
for num in range(start_n,end_n):
  list_data.append(calc(num))
for func in list_data:
  print(func(calc_num))

Decorator(데코레이터)

  1. 어떤 함수를 받아 명령을 추가한 뒤에 이를 다시 함수의 형태로 반환하는 함수
  2. 함수의 내부를 수정하지 않고 기능에 변화를 주고 싶을 때 사용한다.
  3. 함수 앞뒤에 기능을 추가해서 전처리나 후처리에 대한 필요가 있을때 사용한다.
  4. 다른 함수를 꾸며주는 함수
  5. Closure function를 활용
def outer_func(function):
  def inner_func():
    print('전처리')
    function()
    print('후처리')
  return inner_func

def log_func():
  print('무언가 로직을 처리하는 함수')

# 데코레이터 전

decorated_func = outer_func(log_func)
decorated_func()

# 데코레이터로 바꾸면?

@outer_func
def log_func():
  print('후')
log_func()

Decorator(데코레이터) 연습

type_checker 데코레이터를 만들어보자

# type 데코레이터
def type_checker(function):
  def multiple(digit1, digit2):
    if type(digit1) != int or type(digit2) != int:
      print('only integer support')
      return
    function(digit1, digit2)
  return multiple

@type_checker
def mutiple_fnc(digit1, digit2):
  print(digit1 * digit2)

mutiple_fnc(2,4)

*args, **kwargs

parameter는 어떤 형태이든 *args, **kwargs로 표현이 가능하다
데코레이터의 내부함수 파라미터를 (*args, **kwargs)로 작성하면 어떤 함수든 데코레이터로 적용이 가능

  • *args : 여러개가 인자로 들어올 때 함수 내부에서 해당 변수를 '튜플'로 반환
  • **kwargs : 딕셔너리로 반환

type데코레이터를 응용해보도록 하겠다.
파라미터로 전달한 값의 type을 int형이 아닐 때 함수를 그냥 리턴하도록 하겠습니다.

def general_decorator(function):
  def wrapper(*args,**kwargs):
    for num in args:
      if(type(num) != int):
        return '전달받은 인자 값은 정수형만 가능합니다.'
    return function(*args, **kwargs)
  return wrapper

@general_decorator
def calc_square(digit):
  return digit * digit

@general_decorator
def calc_plus(digit1, digit2):
  return digit1 + digit2

@general_decorator
def calc_quad(digit1, digit2, digit3, digit4):
  return digit1 * digit2 * digit3 * digit4

print(calc_square(5))
print(calc_square(5.5))
print(calc_plus(4,6))
print(calc_plus(6,5.4))
print(calc_quad(1,2,3,4))
print(calc_quad(1,2,3.3,4))

Method Decorator

클래스의 method에도 데코레이터 적용 가능하다. 클래스의 메서드의 첫 parameter는 self이므로 데코레이터 작성시에 포함시켜야한다.

왜냐하면 모든 파이썬 클래스의 메서드는 맨 처음 인자가 자기 자신을 나타내는 self 이기 때문

def span_tag(function):
  def func_wrapper(self, *args, **kwargs):
    return '<span>{0}</span>'.format(function(self,*args, **kwargs))
  return func_wrapper

class Person:
  def __init__(self, first_name, last_name, age):
    self.first_name = first_name
    self.last_name = last_name
    self.age = age
  
  @span_tag
  def get_person_info(self):
    return '성 : ' + self.first_name + '/ 이름 : ' + self.last_name + '/ 나이 : ' + self.age
  
chanho = Person('Yoon','Chanho','28')
print(chanho.get_person_info())

다양한 데코레이터

@app.after_request

@app.after_request
def after_request(response):
	return response

HTTP 요청 처리 후 브라우저에 응답하기 전 실행
파라미터로 전달 받은 값을 return 반환 해야함

@app.before_first_request

@app.before_first_request
def before_first_request():

flas실행 한 후 첫 요청 때에만 실행

@app.before_request

@app.before_request
def before_request():

HTTP 요청이 들어올 때마다 실행


참고

2개의 댓글

comment-user-thumbnail
2021년 4월 18일

윤프로의 풀스택 개발 응원합니다!

1개의 답글