티스토리 뷰

📌  데코레이터

이미 정의되어 있는 함수에 기능을 추가하고자 한다면 데코레이터를 사용한다. 가변 인자를 반드시 사용해야 하는 것은 아니나, 여러 가지 상황에 대응할 수 있으므로 추천한다. 

 

 

📍  어떤 함수의 이름을 인자로 받아 꾸며준 후, 다시 해당 함수를 리턴(실행)한다.

📍  데코레이트 할 함수의 함수의 정의부 상단에 @데코레이터명을 추가하는 방법으로 사용할 수 있다.

📍  이미 정의된 함수에 로직을 추가하므로 코드 중복을 최소화하고 재사용성이 향상된다.

📍  데코레이터는 중첩이 가능하다. 하지만 가독성이 떨어지고 디버깅이 어렵다는 단점이 있다.

📍  매개 변수로 *args와 **kwargs를 사용하는 것을 추천한다.

 


👉  데코레이터 만들기

def hello() :
    print('hello')
    
def hi() :
    print('hi')

✔  출력문을 하나씩 가지는 함수에 함수의 시작과 끝을 출력하는 기능을 데코레이터를 사용해 추가해보자.

def deco(func) :

✔  데코레이터 함수는 기능을 확장할 함수를 매개 변수로 갖는다.

def deco(func) :    
    def deco_func() :	#기능이 추가된 함수 
        print('%s()함수 start' %func.__name__)
        func()		#기능 확장을 위해 데코함수 내부에 반드시 기존 함수 실행
        print('%s()함수 end' %func.__name__)
    return deco_func 	#기능이 추가된 함수를 반환

✔  데코레이터 함수 내부에서 기능이 확장된 함수 deco_func()를 새롭게 생성한다.

✔  기능이 확장된 함수 내부에서는 매개 변수로 가져온 함수 func()가 실행되어야 한다.

✔  그러면 hello만 출력할 수 있었던 함수 hello()에 함수의 시작과 끝을 출력할 수 있게끔 기능이 확장된다.

✔  마지막으로 기능이 확장된 함수 deco_func를 반환한다.

deco_hello = deco(hello)
deco_hi = deco(hi)

deco_hello()
deco_hi()

✔  반환된 deco_func를 새로운 변수를 사용해 참조하여 사용할 수 있다.

✔  전체 코드는 아래와 같다

def deco(func) :    
    def deco_func() :	#기능이 추가된 함수 
        print('%s()함수 start' %func.__name__)
        func()		#기능 확장을 위해 데코함수 내부에 반드시 기존 함수 실행
        print('%s()함수 end\n' %func.__name__)
    return deco_func	#기능이 추가된 함수를 반환

def hello() :
    print('hello')
    
def hi() :
    print('hi')

deco_hello = deco(hello)
deco_hi = deco(hi)

deco_hello()
deco_hi()

 

👉 @를 사용해 데코레이터 만들기

def deco(func) :    
    def deco_func() :	#기능이 추가된 함수 
        print('%s()함수 start' %func.__name__)
        func()		#기능 확장을 위해 데코함수 내부에 반드시 기존 함수 실행
        print('%s()함수 end\n' %func.__name__)
    return deco_func	#기능이 추가된 함수를 반환

def hello() :
    print('hello')
    
def hi() :
    print('hi')

deco_hello = deco(hello)
deco_hi = deco(hi)

deco_hello()
deco_hi()

def deco(func) :    
    def deco_func() :	#기능이 추가된 함수 
        print('%s()함수 start' %func.__name__)
        func()		#기능 확장을 위해 데코함수 내부에 반드시 기존 함수 실행
        print('%s()함수 end\n' %func.__name__)
    return deco_func	#기능이 추가된 함수를 반환

@deco
def hello() :
    print('hello')

@deco
def hi() :
    print('hi')

hello()
hi()

✔  기능을 확장하고자 하는 함수 정의 위에 @데코레이터 함수명을 추가한다.

✔  반환받은 함수를 새로운 변수를 사용해 참조하지 않고 기존 함수를 호출하면 확장된 기능이 실행된다.

 

👉  매개변수와 반환 값이 있는 함수의 데코레이터

@deco
def add(a,b) :
	return a + b

✔  매개변수와 반환값이 있는 함수의 데코레이터를 만들어보자.

def deco(func) :
    def deco_func(a,b) :
    	res = func(a,b)
        print('a + b = ',res)
    	return res
    return deco_func
    

✔  기존 함수에 매개 변수와 반환 값이 없었을 때, deco_func 함수에도 매개 변수와 반환 값을 사용하지 않았다.

✔  기존 함수에 매개 변수와 반환값이 있다면, deco_func 함수에도 동일하게 사용해야 한다.

✔  만약 기존 함수의 매개변수가 가변 인자라면 동일하게 사용해야 한다.

✔  이 때, 위치 인수와 키워드 인수를 모두 받을 수 있도록 *args와 **kwargs를 지정한다.

def deco(func) :
    def deco_func(a,b) :
        res = func(a,b)
        print('a + b = ',res)
        return res
    return deco_func
    
@deco
def add(a,b) :
	return a + b
    
add(10,20)

✔  전체 코드와 실행 결과는 위와 같다.

 

👉  매개변수가 있는 데코레이터

def html_tag(tag) :
    def wrapper(func) :
        def deco_func() :
            start_tag = '<'+tag+'>'
            end_tag = '</'+tag+'>'
            return start_tag+func()+end_tag
        return deco_func
    return wrapper

a, b = input().split()
 
@html_tag(a)
@html_tag(b)
def hello():
    return 'Hello, world!'
 
print(hello())

✔  먼저 매개변수를 받는 함수로 감싸주고, 일반적으로 사용하는 데코레이터를 중첩 함수로 정의한다.

✔  중첩 함수만큼 return값이 필요하니 주의하자.

 

👉  여러 데코레이터를 붙이기

def decoA(func) :
    def deco_func(a,b) :
        func(a,b)
        print('a + b = ',a+b)
        print('a - b = ',a-b)
    return deco_func

def decoB(func) :
    def deco_func(a,b) :
        func(a,b)
        print('a * b = ',a*b)
        print('a / b = ',a/b)
    return deco_func

@decoA
@decoB
def num(a,b) :
    print('{0}와 {1}의 사칙연산'.format(a,b))

num(20,4)

✔  여러 데코레이터를 지정하면, 위에서 아래로 데코레이터가 실행된다.

✔  여러 데코레이터를 사용한다는 건 기존의 함수에 기능을 2가지 추가한 것이므로 기존 함수는 1번만 실행된다.

deco_num = decoA(decoB(num))
deco_num()

✔  @를 사용하지 않으면 위의 코드와 동일하게 동작한다.

 

👉 클래스로 데코레이터 구현하기

@Deco
def hello() :
	print(hello)

✔  클래스로 데코레이터를 만들 때에는 인스턴스 함수처럼 호출하게 해주는 특수 메서드 __call__()를 구현해야 한다.

✔  클래스로 데코레이터를 만들 때에도 매개변수의 개수, 형태, 반환 값의 유무를 기존 함수와 동일하게 사용해야 한다.

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

✔  클래스의 인스턴스 변수로 기존 함수명을 넘겨주어야 하기 때문에 생성자를 정의한다.

class Deco :
    def __init__(self, f) :
        self.func = f
    
    def __call__(self) :
        print(__name__,' start')
        self.func()
        print(__name__,' end')

✔  특수 메서드 __call__을 구현할 때에 생성자를 통해 인스턴스 변수로 만들어둔 기존 함수를 반드시 사용하자.

✔  클래스를 사용해 데코레이터를 구현할 때는 메서드에 반환 값이 있을 수는 있지만

✔  __call__()는 객체를 함수 호출 방법으로 호출하므로, 기능이 추가된 함수명을 반환할 필요는 없다. 

class Deco :
    def __init__(self, f) :
        self.func = f
    
    def __call__(self) :
        print(__name__,' start')
        self.func()
        print(__name__,' end')

@Deco
def hello() :
	print('hello')

#객체를 생성하고, 객체를 호출해 확장된 기능의 hello()함수 실행
test = Deco(hello)
test()

#기존 함수를 호출해도 확장된 기능의 hello()함수가 실행
hello()

✔  __call__()를 사용한 덕에 기존 함수를 호출하더라도 Deco 객체를 호출하듯 사용할 수 있다.

 

 

 

 

 

[참고] 엘리스 트랙-혼자 공부하는 파이썬 / 길벗-파이썬 코딩 도장 / 엘리스 트랙 - 예제 중심의 파이썬 입문 / 위키백과

댓글
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
최근에 올라온 글
글 보관함
Total
Today
Yesterday