본문 바로가기
관리자

Programming-[Backend]/Python

Decorator 개념과 적용, @wraps

728x90
반응형

Decorator

decorator는 함수를 파라미터로 받아서 그 함수를 실행하면서 다른 기능들을 실행할 수 있도록 해주는 패턴이다. 여기서 다른 기능들이란 인자로 받는 함수의 실행 전, 후로 로그인, try-catch문 등 반복적으로 수행되는 작업을 의미한다.

 

참조1의 예시가 가장 이해하기 좋고 일반적인 예제인것 같다. decorator_exam 메서드에서 인자로 받아오는 함수 func를 내부 함수 decorator_func에서 다른 기능들과 함께 실행한 후 리턴하도록 만들어준다. 그러면 @decorator_exam을 위에 써준 함수들을 실행했을 때, 해당 기능이 작동한다.

def decorator_exam(func):
    def decorator_func():
        print('함수 시작')
        func()
        print('함수 끝')
    return decorator_func()

@decorator_exam
def hi_function():
    print('hi')

## '함수 시작'
## 'hi'
## '함수 끝'

@decorator_exam
def hello_function():
    print('hello')
    
## '함수 시작'
## 'hello'
## '함수 끝'

 

클래스 형태로 만들 수도 있다고 한다. init 메서드에서 멤버변수로 함수인 func를 받아오고, call 메서드에 함수와 함께 실행할 다른 기능들을 작성해준다.

class DecoratorExam:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print('함수 시작')
        self.func(*args, **kwargs)
        print('함수 끝')

 

@Wraps

참조 1의 조금 더 실질적인 예제 코드이다. login_test_user 메서드에서 파이썬 크롤링 코드에서 사용되는 코드를 작성했다. 로그인 버튼을 누르고, id, pw를 입력한다. 그리고 login_test_user_with_browser 메서드는 Decorator를 정의한다. 상기 login_test_user 메서드를 실행한 후 인자로 받아온 method를 실행한다.

from functools import wraps

def login_test_user(self):
    if hasattr(self, 'browser'):
        self.browser.get(self.live_server_url)
        self.browser.find_element_by_id('login_button').click()
        self.browser.find_element_by_id('id_username').send_keys(self.test_user.username)
        self.browser.find_element_by_id('id_password').send_keys('kboard123')
        self.browser.find_element_by_class_name('btn-primary').click()

def login_test_user_with_browser(method):
    @wraps(method)
    def _impl(self, *args, **kwargs):
        login_test_user(self)
        method(self, *args, **kwargs)

    return _impl

 

여기서 @wraps(method)는 실제 실행 시 @decorator가 실행한 메서드의 이름으로 실행되도록 한다. @wraps가 없으면 아래 실행 코드에서 @login_test_user_with_browser 데코레이터에 의해서 디버깅 시에 '_impl' 이라는 이름의 메서드가 실행된다. 이러면 어떤 메서드에 데코레이터를 적용하더라도 '_impl'이라는 메서드가 실행되는 것으로 보인다. @wraps를 적용하면 인자로 받아온 함수인 test_delete_post 메서드가 실행되는 것으로 보여진다.

class DeletePostTest(FunctionalTest):
    @login_test_user_with_browser
    def test_delete_post(self):
        ...

 

 

 

참조

1. decorator 개념 및 적용, @wraps 예제

https://cjh5414.github.io/python-decorator/

 

728x90
반응형