개발자의 길

[Python3] Decorator(장식자) 란 무엇인가?

토아드 2022. 3. 6. 22:45
반응형

데코레이터의 작성 방법

 

 개발을 하다 보면 여러 함수에 하나의 동작을 추가시키고 싶을 때가 있다. 대표적으로 함수의 시간 측정이 필요해서 함수 동작 앞,뒤로 로그를 찍는 등의 작업이 있다. 하지만 일일히 손으로 로그를 추가하는 건 매우 비효율적이며 유지보수도 힘들다.

 파이썬에서는 이러한 작업을 데코레이터라는 기법을 이용해서 간단히 해결 할 수 있다. 우선 아래 예제를 보자.

 

import datetime

def doSomething():
	print("do something")
    
#우리는 위 함수를 아래와 같이 바꾸고 싶을 떄가 있다.

def doSomethingWithLog():
	print("start :" + str(datetime.datetime.now() ) )
	print("do something")
	print("end :" + str(datetime.datetime.now() ) )
    
    
#데코레이터를 이용하면 아래와 같이 해결 가능하다

def logTime( function ):
    def warpper():
        print("start :" +  str(datetime.datetime.now() ) )
        function()
        print("end :" +  str(datetime.datetime.now() ) )
    return warpper

#이부분이 데코레이터다
@logTime
def doSomethingWithDeco():
	print("do something")
    
    
doSomethingWithDeco()

#실행 결과
# start :2022-03-06 20:43:01.076558
# do something
# end :2022-03-06 20:43:01.078353

 

데코레이터는 이전에 포스팅한 Nested function, Closure function 과 연관이 있는데, 이 두 개념으로 데코레이터와 같은 효과를 일으키는 코드를 작성할수 있기 때문이다. 아래 코드를 보면 Nested function 과 Closure function 기법을 활용해 데코레이터와 같은 효과를 일으키는 것을 알 수 있다.

 

import datetime

def addLog_creator( function ):
    def warpper():
        print("start :" + str( datetime.datetime.now()))
        function()
        print("end :" + str( datetime.datetime.now()))
    return warpper

def printInfo():
    print("print some information")


printInfoWithLog = addLog_creator( printInfo )
printInfoWithLog()

#실행 결과
# start :2022-03-06 20:46:22.978215
# print some information
# end :2022-03-06 20:46:22.979520

 

파라미터를 받을 수 있는 데코레이터 작성하기

 

위 코드에서는 인자가 없는 함수를 데코레이트 하고 있는데, 인자가 있는 경우는 데코레이터를 정의할 때 wrapper 함수에 파라미터를 추가하면 된다. 물론 가변 인자를 받게 정의할 수도 있는데, 여기서는 가변 인자를 받는 데코레이터를 정의 해 보겠다.

 

def log_creator( function ):
	def warpper( *args, **kwargs ):
    	print("decoratd")
        function( *args, **kwargs )
    return warpper
    
    
@log_creator
def printInfo( msg ):
	print("log : " + msg)
    
    
printInfo( "this is python" )


#실행결과
# decoratd
# log : this is python

 

데코레이터 중첩시키기

데코레이터는 아래와 같이 중첩되서 사용될 수도 있다. 

 

def deco1( function ):
    def warpper():
        print("deco1")
        function()
    return warpper


def deco2( function ):
    def warpper():
        print("deco2")
        function()
    return warpper


@deco1
@deco2
def printInfo():
	print( "Informations..")
    
    
printInfo()

#실행결과
# deco1
# deco2
# Informations..

 

Method Decorator

클래스에 데코레이터를 사용하고 싶을 경우, warpper 의 파라미터에 self 를 명시하고, wrapper 에서 데코레이터가 인자로 받은 함수를 호출할 때에 self 를 파라미터로 넣어줘야 한다.

 

def classDeco( function ):
    def wrapper( self ):
        print("hey")
        function( self )
    return wrapper

class Human:
    def __init__(self, name):
        self.name = name
    
    @classDeco
    def printName( self ):
        print( "name:" + self.name )
        
me = Human("smith")
me.printName()

#실행 결과
# hey
# name:smith

 

'데코레이터'에 인자 받도록 만들기

데코레이터를 사용하면서 데코레이터 자체에 인자를 받게 만들 수도 있다. 중첩 함수를 2단계나 중첩시키는 복잡함이 있긴 한데 closure function 개념으로 접근하면 이해할 수 있을 것이다. 아래 코드를 보자

def deco( number ):
    def out_wrapper( function ):
        def in_wrapper( *args, **kwargs ):
            print( "deco key is :{}".format(number) )
            function( *args, **kwargs)
        return in_wrapper
    return out_wrapper
            
@deco(2)
def printInfo( name ):
    print( "information of " + name )
    
    
printInfo( "smith" )

# 실행 결과
# deco key is :2
# information of smith
반응형