[Python] Decorator

haejun-kim·2020년 6월 28일
0

[Python]

목록 보기
9/19
post-thumbnail
post-custom-banner

데코레이터

이미 작성된 코드에 새로운 기능을 추가하여 함수 기능을 확장시키는 개념

동작 원리

파이썬에서의 데코레이터는 함수의 기능을 확장시키는 개념인데, 어떤 방식으로 함수를 확장시킬 수 있는지 동작 원리에 대해서 간단히 확인해보자.

  • 파이썬에서 함수는 일급객체로 취급
  • 클로저를 사용하여 함수 내 함수를 정의할 수 있음

위의 두가지의 이유로 파이썬에서 데코레이터라는 개념을 사용하여 함수의 기능을 확장할 수 있다.
예를 들어,

def outer_function(message):
    def inner_function():
        return "내부 함수 메세지 : {}".format(message)
    return inner_function

c = outer_function("Hello")

위의 코드의 동작을 확인하기 위해

print(dir(c))

dir(c) 명령어를 사용해 변수 c가 어떻게 구성되어 있는지 확인해보자

뭔지 모를 여러가지 명령어들이 많이 나오는데 여기서 우리는 첫번째 줄 네번째에 있는 __closure__만 확인하면 된다.

__closure__의 타입과 길이를 확인해보자.

print(type(c.__closure__))
print(len(c.__closure__))

길이가 1튜플형태의 데이터이다. 이 데이터 안에는 또 무엇이 들어있는지 확인해보면

print(dir(c.__closure__[0]))

앞 뒤에 __가 없는 cell_contents라는 변수가 있는걸 확인할 수 있다. 이 변수에는 무엇이 들어있는지 분석해보면

print(c.__closure__[0].cell_contents)


가 나온다. 이 Hello는 서두에 내가 정의한 c라는 변수에 함수를 담고 그 함수의 매개변수값으로 설정한 단어가 들어가있다.

고로, outer_function 함수를 실행함으로 인해서 c 안에 __closure__가 구현이 되었고 그 안에 cell_contents라는 변수에 함수에서 넘긴 인자값(Hello)을 관리하고있다.
이런 방식으로 데코레이터는 함수의 기능을 확장할 수 있게 된다.

사용 예제

데코레이터의 동작 원리를 살펴봤으니 이제 사용 예제를 보자
하나의 함수를 만들고 그 함수의 동작 시간(함수의 실행 시작부터 끝날 때 까지의 시간)을 측정해보려 한다.

import time

def time_checker(func):
    def inner_function(*args, **kwargs):
        start_time = time.time()
        results = func(*args, **kwargs)
        end_time = time.time()
        print("함수 {} 동작시간 : {}".format(func.__name__, end_time-start_time))
        return results
    return inner_function

@time_checker
def test1():
    for i in range(3):
        time.sleep(0.1)

@time_checker
def test2():
    for i in range(5):
        time.sleep(0.1)


test1()
test2()
  1. 함수의 시간을 측정하기 위해 timeimport해준다.
  2. time_checker()라는 함수를 정의하여 매개변수값으로 함수를 받도록 한다. 그리고 내부 함수를 정의해준다.
  3. 함수의 end_time에서 start_time을 빼주면 실제 동작한 시간이 몇초동안인지 확인할 수 있으며 그 값을 리턴해주면 된다.

위와같은 기능을 하는 함수를 만들었고 이제 test1(), test2() 함수를 만들어서 실행시켜보자. 함수 정의한 부분 위에 @데코레이터 할 함수 이름 을 적어주면 된다.
그럼 test1(), test2() 함수는 time_check 함수를 데코레이팅 한게 된다.

동작결과

주의사항

데코레이터를 사용할 때 꼭 신경써야 할 부분이 있다.
바로 데코레이터를 사용하면 데코레이터를 사용한 함수는 속성이 사라져버리는 것이다.

@time_checker
def test1():
    """
    테스트 함수입니다.
    """
    for i in range(3):
        time.sleep(0.1)

print(test1.__name__)
print(test1.__doc__)

위에 사용된 예제에서 docstring을 추가한 뒤, test1함수의 __name____doc__을 출력해보았다.

분명 test1함수의 이름과 독스트링을 프린트했는데, 이름은 inner_function, 독스트링은 None으로 나온다. 함수가 데코레이팅되면서 함수의 속성까지 같이 데코레이팅 돼버린 것이다.
이러한 부분을 방지하기 위해서 파이썬에서 제공하는 라이브러리가 있다.

from functools import wraps

해당 라이브러리를 사용하면 함수를 데코레이팅 하더라도 함수의 속성을 기존대로 유지시킬 수 있다.

from functools import wraps # -- added
def time_checker(func):
    @wraps(func) # -- added
    def inner_function(*args, **kwargs):

기존의 함수에서 두 줄 추가해주었다.

이제 기존 함수의 속성을 그대로 유지하는것을 확인할 수 있다.

고로, 데코레이터를 사용할 때는 꼭 @wrap 해주는것을 주의하자

post-custom-banner

0개의 댓글