달나라 노트

Python Basic : decorator (@) 본문

Python/Python Basic

Python Basic : decorator (@)

CosmosProject 2021. 9. 14. 23:23
728x90
반응형

 

 

 

Python의 decorator라는 것에 대해 알아보겠습니다.

Python을 하다보면 아래처럼 @로 시작하는 부분을 볼 수 있습니다.

 

@test_function
def sub_func():
    print('This is test message.')

이렇게 @(At sign)을 이용하는 것을 decorator라고 합니다.

 

 

decorator가 뭔지를 먼저 간단하게 말하자면 '기존 함수에 decorator 함수를 추가해서 기존 함수를 장식(decorate)해주는 기능'이라고 할 수 있습니다.

지금은 이게 무슨 말인지 몰라도 됩니다.

 

한번 예시를 보시죠.

 

 

 

 

 

def main():
    print('main function started')

main()



-- Result
main function started

위 코드를 실행하면 main함수가 실행되면서 main function started라는 구문이 출력됩니다.

아주 간단한 함수죠.

 

근데 어떠한 상황에 의해 main function을 약간 수정할 필요가 생겼습니다.

 

 

 

 

 

def main():
    print('user_1130 login')
    print('main function started')
    print('user_1130 logout')

main()



-- Result
user_1130 login
main function started
user_1130 logout

그래서 위처럼 수정하였습니다.

main function started라는 구문 앞 뒤에 user_1130이 로그인했다, 로그아웃했다라는 print 구문을 추가했습니다.

 

보통 이렇게 어떤 함수에 추가적인 기능이 필요한 경우 해당 함수 자체를 수정하게되죠.

 

위 경우는 main 함수 1개만 수정하면 됩니다.

근데 만약에 main함수, main_1 함수, main_2 함수, ... 등 코드를 작성하며 만든 수많은 함수들의 내용에 해당 함수가 시작될때 user의 login 메세지를 출력하고 해당 함수의 끝부분에 user의 logout 메세지를 추가해야한다면 어떻게 해야할까요?

 

예를들어 위처럼 고쳐야 할 함수가 100개가 있다면 그 100개의 함수 각각에 user_1130 login / user_1130 logout 메세지를 출력해주는 print 구문을 일일이 다 적어야 할 것입니다.

 

상당히 불편한 일이 아닐 수 없죠.

이때 사용할 수 있는 Python의 특징이 있습니다.

바로 어떤 함수가 parameter로서 다른 함수를 받을 수 있다는 것이지요.

 

 

 

 

 

 

 

def decorator(func):
    def decorated():
        print('user_1130 login')
        func()
        print('user_1130 logout')
    return decorated

def main():
    print('main function started')

main = decorator(main)

main()




-- Result
user_1130 login
main function started
user_1130 logout

위 코드를 봅시다.

main function은 main function started라는 구문만 print하도록 되어있습니다.

 

한 가지 차이는 바로 decorator 함수가 생겼다는 것이죠.

 

decorator 함수를 봅시다.

decorator 함수 내부에서는 decorated란 함수를 생성하고 생성된 decorated 함수를 return해주죠.

 

그리고 decorated 함수는 decorator 함수가 parameter로서 받은 함수(func) 앞 뒤에 user_1130 login, user_1130 logout이라는 메세지 print 구문을 추가해줍니다.

 

그래서 main = decorator(main) 이 부분을 보면

main function started라는 메세지만을 출력해주던 main 함수를

decorator 함수에 넣어서 user_1130 login / user_1130 logout이라는 추가 print문을 가지도록 장식(decorate) 해준 후 이것을 main 변수에 재할당하여 새로운 main 함수를 만들어주고있습니다.

 

그래서 만들어진 main 함수를 실행시키면 우리가 원하던대로 login, logout 메세지까지 잘 뜨게되죠.

 

 

 

자, 이 얘기를 왜 하냐구요?

이제 저희가 처음에 얘기했던 @(At sign)을 사용할 때가 왔습니다.

 

바로 위에서 다뤘던 코드를 아래처럼 바꿔보겠습니다.

기능과 코드가 실행되는 과정은 거의 100% 동일하다고 보시면 됩니다.

 

def decorator(func):
    def decorated():
        print('user_1130 login')
        func()
        print('user_1130 logout')
    return decorated

@decorator
def main():
    print('main function started')

main()





-- Result
user_1130 login
main function started
user_1130 logout

결과를 보면 완전히 똑같죠?

결과만 같은 것이 아니라 코드가 의미하는 바도 완전히동일합니다.

 

 

위에서 본 2가지 동일한 예시의 중요한 부분을 비교해봅시다.

def main():
    print('main function started')


main = decorator(main)

 

@decorator
def main():
    print('main function started')

위 2가지 부분은 동일합니다.

 

즉, 우리가 Python 코드에서 @(At sign)으로 시작하여 그 뒤에 함수를 적는다는 것의 의미는

@ 에 적힌 함수(decorator)의 parameter로 @ 다음에 정의되는 함수(main)를 전달하여

decorator 함수의 기능이 추가된 main함수로 장식(decorate)하라는 뜻입니다.

 

이렇게 어떤 함수의 기능에다가 decorator의 기능을 추가하여 장식해준다는 것을 보면

앞에서 말했던 다음의 내용을 이제 이해하실 수 있을겁니다.

 

"decorator가 뭔지를 먼저 간단하게 말하자면 '기존 함수에 decorator 함수를 추가해서 기존 함수를 장식(decorate)해주는 기능'이라고 할 수 있습니다."

 

 

처음 보면 복잡할 수 있지만 위 내용을 곰곰이 되짚어보면 금방 이해가 되실 겁니다.

 

이렇게 Python 함수들에 대해 decorator가 무엇을 의미하며 어떻게 사용할 수 있는지를 알아봤습니다.

 

 

 

 

 

 

 

 

 

엄청 자주 사용되는 내용은 아니지만 decorator는 class에서도 사용할 수 있습니다.

 

위 내용을 이해하셨다면 아래 코드도 이해하실 수 있을겁니다.

class Decorator:
    def __init__(self, f):
        self.func = f

    def __call__(self, *args, **kwargs):
        print('user_1130 login')
        self.func(self)
        print('user_1130 login')

class MainClass:
    @Decorator
    def main(self):
        print('main function started')

    @Decorator
    def sub_test(self):
        print('sub_test function started')

cls_main = MainClass()
cls_main.main()
print('')
cls_main.sub_test()




-- Result
user_1130 login
main function started
user_1130 login

user_1130 login
sub_test function started
user_1130 login

class의 decorator가 function에서 사용했던 decorator와 다른 점은

Decorator class에서 __call__의 형태로 decorator function을 설정해줘야 한다는 것입니다.

 

또한 Decorator class의 __init__ 함수의 parameter 중 f가 의미하는 것은 외부에서 받을 함수(main, sub_test 등)를 의미한다고 볼 수 있습니다.

 

 

 

 

 

 

728x90
반응형
Comments