728x90

시작하기 전에

데코레이터란 flask로 앱을 시작할때 ' @app.route('/')' 이 부분에 대한 것이다.

함수에서 함수자체를 인자로 받아서 명령을 실행한 뒤 다시 함수의 형태로 반환하는 경우이다.

 

보통 함수의 내부를 수정하지 않고, 기능에 변화를 주고 싶을 때 사용한다.

 

데코레이터를 이해하기에 앞서 연관된 개념인 first class 함수와, closure에 대해서 먼저 정리해보고자 한다.

 

중첩 함수 (Nested function)

: 함수 내부에 또 다른 함수가 정의된 것이다. 

: 중첩함수/ closure 두가지 개념이 중첩함수와 관련되어 있다.

1
2
3
4
5
6
7
8
9
10
def outer_func(num):
    def inner_func():
        print(num)
        return 'a'
    
    return inner_func
 
fn = outer_func(10)   
print(fn())             
cs

-> 여기서 inner_func이 중첩함수이다.

# 중첩함수는 함수 밖에서는 호출 불가 (outer_func 함수 안에서 선언되었으니, outer_func 함수 안에서만 호출 가능)

 

First-class function

  • 함수 자체를 변수에 저장 가능

  • 함수의 인자에 다른 함수를 인수로 전달 가능

  • 함수의 반환 값(return 값)으로 함수를 전달 가능

이는 파이썬이 객체지향적언어이기 때문이며, 따라서 비슷한 성격의 언어들은 First-class 함수를 지원한다.

 

python, Go, javascript, Kotlin 은 First-class 함수 지원
C 언어등은 First-class 함수 미지원

 

Closure function

    • First-class 함수와 유사한 개념
    • 외부함수의 인자에 대해 내부함수가 접근할 수 있는 것
    • 내부함수에서 외부함수의 인자를 사용하고 있으면 closure의 개념을 활용한 것이라고 이해하면 된다.
1
2
3
4
5
6
7
def outer_func(num):
 
    def inner_func():
        print(num)
        return '안녕'
    
    return inner_func              
cs

->위의 예제에서 inner_func이 바로 closure 임

-> outer_func(10) 호출 종료시 num 값은 없어졌으나, closure_func()으로써 inner_func이 호출되면서 이전의 num값(10)을 사용함

 

데코레이터 (Decorator)

: 위에서 설명한 중첩함수로써, 외부함수의 인자가 함수이고, 그 내부함수에서 클로저로써 이 함수인자를 사용하는 경우 활용된다.

: 다음과 같이 @decorator_func 부분이 데코레이터이다.

1
2
3
4
5
6
7
# 데코레이터 작성하기
def datetime_decorator(func):           # <--- datetime_decorator 는 데코레이터 이름,
    def wrapper():                      # <--- 호출할 함수를 감싸는 함수
        print ('time ' + str(datetime.datetime.now())) # <--- 함수 앞에서 실행할 내용
        func()                          # <--- 함수  
        print (datetime.datetime.now()) # <--- 함수 뒤에서 실행할 내용
    return wrapper                      # <--- closure 함수로 만든다.
cs

-> 데코레이터 적용 후, 

1
2
3
4
5
6
# 데코레이터 적용하기
@datetime_decorator    # @데코레이터
def logger_login_david():
     print ("David login")
 
logger_login_david()
cs

-> 즉 반복될 함수와 그 안에 인자로 들어갈 함수의 코드 작성 및 관계 이해가 좀 더 용이하다. 

-> 위의 예시에서 logger_login_david() 가 datetime_decorator( )의 함수인자인 셈이다.

 

 

내부함수에 별도의 변수가 있는 함수에 Decorator 적용하기

1
2
3
4
5
6
7
8
# 데코레이터
def outer_func(function):
    def inner_func(digit1, digit2):
        if digit2 == 0:                      
            print('cannot be divided with zero')
            return
        function(digit1, digit2)
    return inner_func
cs

 

1
2
3
4
# 데코레이터 사용하기 (유효성 검사)
@outer_func
def divide(digit1, digit2):
    print (digit1 / digit2)
cs

->외부함수는 함수인자를 받고, 내부함수가 변수를 인자로 받는 경우, 데코레이터 함수와 

인자함수 코드를 작성할때, 인자 함수에 내부함수의 변수를 넣어서 기재하면된다.

 

 

내부함수의 변수가 정해지지 않은 경우 Decorator 만들기

->이는 자바스크립트의 spread parameter을 떠올리면 쉽다

->정해지지 않은 인자를 표현하고 싶을 때는 (args, **kwargs) 로 표현 가능

 

한 함수에 데코레이터 여러 개 지정하기

  • 함수에 여러 개의 데코레이터 지정 가능 (여러 줄로 @데코레이터를 써주면 됨)
  • 데코레이터를 나열한 순서대로 실행됨

 

Method Decorator

: 클래스의 method에도 데코레이터 적용 가능

1
2
3
4
5
# 데코레이터 작성하기 (for method)
def h1_tag(function):
    def func_wrapper(self*args, **kwargs):            
# <--- self 를 무조건 첫 파라미터로 넣어야 메서드에 적용가능
        return "<h1>{0}</h1>".format(function(self*args, **kwargs))  
# <--- function 함수에도 self 를 넣어야 함
    return func_wrapper
cs
1
2
3
4
5
6
7
8
9
# 클래스 선언시 메서드에 데코레이터 적용하기
class Person:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name
 
    @h1_tag
    def get_name(self):
        return self.first_name + ' ' + self.last_name
cs

-> h1_tag함수가 데코레이터 함수이며, get_name 함수가 h1_tag의 함수 인자이다. 

그리고 get_name 인자함수는 person의 메소드인 init의 값을 사용하고 싶다. 

이때 활용되는 것이 self 인 것이다. 

 

 

외부함수에 변수인자가 파라미터인 Decorator 만들기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def mark_html(tag):
    def outer_wrapper(function):
        def inner_wrapper(*args, **kwargs):
            return '<' + tag + '>' + function(*args, **kwargs) + '</' + tag + '>'
        return inner_wrapper
    return outer_wrapper



@mark_html('b')
def print_bold(title):
    return title
 
cs

-> 위의 예제처럼 중첩을 한 번 더 진행하고, 데코레이터 함수를 실행할 때, 그 인자에 외부함수에 넣을 변수 인자를 넣으면 된다.

 

728x90

+ Recent posts